_doc in request body - node.js

I am trying to send an object created by MongoDB model as a body of post request in a jest test.
Here is the test:
test("post request successfully creates a new blog", async () => {
const newBlog = new Blog({
title: "My mundane day at home 3",
author: "AbG",
url: "https://www.google.co.in/",
likes: 11,
});
await api
.post("/api/blogs")
.send(newBlog)
.expect(201)
.expect("Content-Type", /application\/json/)
.catch((err) => console.log(err));
const blogs = await Blog.find({});
expect(blogs).toHaveLength(initialBlogs.length + 1);
});
As you can see, I am sending the newBlog as a body of request. But when I receive it in controller, the newBlog is present in the request.body._doc instead of request.body.
I think this has something to do with the mongoose model of blog.
const blogSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
author: {
type: String,
required: true,
},
url: {
type: String,
required: true,
},
likes: {
type: Number,
required: false,
default: 0,
},
});
module.exports = mongoose.model("Blog", blogSchema);
I cannot understand why this is happening.

I found out where I was going wrong.
In the test case, I was creating a mongodb schema instance and passing it as body of request instead of a plain JS object. And in the express app, I was trying to access the request body as a JS object. So that created a confusion.
The changes that I did:
In test:
Instead of
const newBlog = new Blog({....})
I did
const newBlog = {....}
So, I avoided passing mongodb object as request body and created the mongodb object using the constructor immediately before where I needed it.
Here is the post route:
blogsRouter.post("/", async (request, response) => {
const blog = new Blog(request.body);
const savedBlog = await blog.save();
response.status(201).json(savedBlog);
});

Related

Update multiple documents instance in MongoDb collection with Mongoose. save() is not a function

Mongoose newbe here. I got the following function to update the references (deleting them) in the document Post when a Tag is deleted. When I call my GraphQl API this is what I got:
message": "posts.save is not a function"
The function in my gql resolver:
async deleteTag(root, { id }, context) {
const posts = await Post.find();
const tag = await Tag.findById(id);
if(!tag){
const error = new Error('Tag not found!');
error.code = 404;
throw error;
}
posts?.forEach(async (post) => {
await post.tags.pull(id);
})
await posts.save()
await Tag.findByIdAndRemove(id);
return true;
}
This is the Post model:
const PostSchema = new Schema({
body: {
type: String,
required: true
},
tags: {
type: [Schema.Types.ObjectId],
ref: 'Tag',
required: false
},
});
and this is the Tag model:
const TagSchema = new Schema(
{
name: {
type: String,
required: true
},
},
{ timestamps: true }
);
Looks like I can't call the method save() on the array of objects returned by Exercise.find()
I used the same pattern in other functions, the difference is that there I used .findById()
Any solution? Advice and best practice advide are super welcome.
You have to save the posts individually:
posts?.forEach(async (post) => {
await post.tags.pull(id);
await post.save();
})
Or use Model.updateMany() combined with the $pull operator.
FWIW, you should probably limit the number of matching Post documents by selecting only documents that have the specific tag listed:
await Post.find({ 'tags._id' : id });

Node.js: Cast to ObjectId failed for value "new" (type string)

I am rather new to node and am having an issue that is a bit difficult for me to decipher. I am trying to make a reddit clone. The part that I am working on right now is to allow users to view posts other users have posted. I was trying to implement a new route by pasting in this bit of code
app.get('/', (req, res) => {
Post.find({}).lean()
.then((posts) => res.render('posts-index', { posts }))
.catch((err) => {
console.log(err.message);
})
})
However, I am getting this error when I run it:
Cast to ObjectId failed for value "new" (type string) at path "_id" for model "Post"
It seems like something is wrong with my post.js model, but I can't identify it. Could someone review this and help me identify the triggering factor?
const { Schema, model } = require('mongoose');
const postSchema = new Schema({
title: { type: String, required: true },
url: { type: String, required: true },
summary: { type: String, required: true },
}, { timestamps: true });
module.exports = model('Post', postSchema);

Axios react node post request is giving a 422 (Unprocessable Entity)

I am trying to save the desired book to my MongoDB database when I press the saved button I get a 422 error I pass in the data as an object but for some reason, the data doesn't appear in the response back from the server The data is being passed to the Axios call but for some reason, the data property always returns an empty object,
The save handler
const handleSave = (event) => {
event.preventDefault();
let save = books.filter((book) => book.id === event.target.id);
// console.log(save);
// const da/ta = ;
// console.log(data);
API.saveBook({
title: save[0].title,
authors: save[0].author,
description: save[0].description,
image: save[0].image,
})
.then((res) => {
alert("book saved");
console.log(res);
// console.log(data);
})
.catch((err) => {
// console.log(data);
console.log("book not saved");
console.log(err.response);
});
};
This is the book model and the heroku link where you can see what is being logged out
const bookSchema = new Schema({
title: { type: String, required: true },
authors: [{ type: String, required: true }],
description: { type: String, required: true },
image: { type: String },
date: { type: Date, default: Date.now },
});
Heroku Link
github
I have console.logs in my inspect so you can check those out to see the response im getting back
I have cloned this repository and tested on both your Heroku link and locally, and cannot recreate the error locally. I suspect something to do with the MongoDB server rather than a code issue. I recommend you test creating a record in the live/Heroku-attached MongoDB server using an alternative method.
Thanks,
Will Walsh
Looks like volumeInfo.description is undefined for some books. The API returns a 422 error since description is required but is not present in the request payload. You could pass a default description if the book doesn't have one.
result = {
// ...
title: result.volumeInfo.title,
description: result.volumeInfo.description || "This book doesn't have a description",
// ...
}
Or you could remove the required validation for the description field if it's not an issue.
I would recommend you rename author to authors in the result object for clarity.
result = {
// ...
authors: result.volumeInfo.authors,
// ...
}

Having trouble posting to Mongo DB after external API Call

I'm attempting to make a fetch call to News API and populate my Articles collection in Mongo DB with the data I fetch. I have two files: articles.js, and article.js. - my schema looks like the following:
// Article.js
const ArticleSchema = new Schema({
title: {
type: String,
required: true
},
author: {
type: String,
required: true
},
description: {
type: String,
required: true
},
url: {
type: String,
required: true
},
datePublished: {
type: String,
required: true
},
source: {
type: String,
required: true
}
})
In articles.js, this is what my route looks like. My logic is that I make my fetch call, wait for the JSON to return, then iterate thru that json and map all the information I need to new story objects. Once I have those objects - I want to create a new instance of an Article for each, then post them to my Articles collection in Mongo DB.
/ #route POST /articles -> adds new instance of an article to the database
router.post('/', async (req, res) => {
try {
const res = await fetch('https://newsapi.org/v2/top-headlines?country=us&category=entertainment&apiKey=63c967f7cbd84c11b263b4e4758f1693');
const data = await res.json();
data.articles.forEach(article => {
const storyData = {
title: article.title,
author: article.author,
description: article.description,
url: article.url,
datePublished: article.publishedAt,
source: article.source.name
}
// console.log(storyData)
new Article(storyData)
.save()
})
} catch (err) {
console.error(err.message)
res.status(400).json({ message: err.message })
}
})
After my forEach loop, I get the objects like so:
{
title: `'Matrix 4' Style "Shifted" From Original Trilogy, Says Neil Patrick Harris - Hollywood Reporter`,
author: 'Ryan Parker',
description: 'The actor, who appears in the fourth installment, called the upcoming film "ambitious."',
url: 'https://www.hollywoodreporter.com/heat-vision/matrix-4-style-shifted-from-original-trilogy-says-neil-patrick-harris',
datePublished: '2020-09-16T17:54:26Z',
source: 'Hollywood Reporter'
}
I'm able to get the objects I'd like with the data I need, however when I attempt to call .save(), I receive validation error similar to the following:
"ValidationError: article validation failed: author: Path author is required."
I'm thinking that it may have to do with the fact that when I make post to the DB, the request should list something along the lines of "title: req.body.title", etc... Any thoughts on what could be causing this? My goal is to simply post these to the DB as I create them. Thanks!!!

How to upload image to mongodb using node.js, express.js

Am trying to upload image to the task collection in my mongodb but keep getting an error message.
here is my task model
const mongoose = require('mongoose')
const taskSchema = new mongoose.Schema({
description: {
type: String,
trim: true,
required: true
},
completed: {
type: Boolean,
default: false
},
owner: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
},
avatar: {
type: Buffer
}
},
{
timestamps: true
})
const Tasks = mongoose.model('Tasks', taskSchema )
module.exports = Tasks
Here is my task router code
router.post('/tasks/me/avatar', auth, upload.single('avatar'), async (req, res) => {
const buffer = await sharp(req.file.buffer).resize({ width: 250, height: 250 }).png().toBuffer()
req.tasks.avatar = buffer
await req.tasks.save()
res.send()
}, (error, req, res, next) => {
res.status(400).send({ error: error.message })
})
error message
[![enter image description here][1]][1]
It keeps giving me an error message of cannot set property 'avatar' of undefined, meanwhile the avatar field has already been defined in the task model.
How can I fix this issue, thanks.
Ok after looking at your answer i'll propose this solution.
Instead of:
req.tasks.avatar = buffer
await req.tasks.save()
Try this
const query = {id: docId}; //prepare a query to find the doc
Task.findOneAndUpdate(query, { $set: {avatar: buffer} }, ()=>{
/Your callback function to run after completed
});
//Then call findOneAndUpdate method from any mongoose.model
//Which will do exactly what its name says:
//Finding ONE element, the first one matching your query
//And then update any values inside $set
//And after that you may use any callback function
Let me know if that helps you out.

Resources