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

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,
// ...
}

Related

_doc in request body

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);
});

MongoDB E11000 duplicate key error collection with no unique field in schema

I'm setting up a simple blog form with title, content, and json base64 image data to pass to a local MongoDB server. This is my post route:
router.post('/:userName/posts/new', isAuthenticated, async (req, res) => {
let parsedImage = req.body.file != null && req.body.file != '' ? JSON.parse(req.body.file).data : ''
const newPost = new Post({
title: req.body.title,
content: req.body.content,
imageJson: parsedImage
})
try {
await newPost.save()
res.redirect("/account")
} catch (e) {
console.log(e)
res.redirect("/home")
}
})
And this is my schema model:
const mongoose = require('mongoose');
const postSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
content: {
type: String,
required: true,
},
imageJson: {
type: String
}
});
module.exports = mongoose.model('Post', postSchema)
no unique: true as you can see. The first time I tried running this, it ran perfectly, redirecting directly to the /account page, as intended in the try catch block. But, on a second attempt, using a different image, different content, and different title, it threw this error:
MongoError: E11000 duplicate key error collection: fullstacksus.posts index: userName_1 dup key: { userName: null }
I have no idea what's going on since userName is a database name used in my user account route, but not in my post-making route. Why is it showing up again? Thanks for your answers.
In your MongoDB there is a unique index on {userName: 1}. Since your schema does not have a userName field, it is missing, i.e. null, in every document, so the index rejects it.
Use the mongo shell to connect and drop the index.

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.

Completely stuck. (MongoDB / MERN issue)

Im creating an app, and right now I'm working on a feature that takes in user input, and stores it in the database. I've done this plenty of times on other projects, but have come to a problem that I just can't solve.
So far, when a user types in their info and hit's enter, the data is sent to the back end, and start's to head towards the endpoint. I have a controller set up with my 'create' function. It is the following:
const db = require('../database/models');
module.exports = {
// create: function(req, res) {
// console.log('hit controller');
// db.Listing
// .create(req.body)
// .then(dbModel => res.json(dbModel))
// .catch(err => res.status(422).json(err));
// }
create: function(req, res) {
console.log('!!!!HERE:CONTROLLERS' + req.body.receiver);
db.Listing
.create(req.body)
.then(function(dbModel) {
console.log(dbMessage);
res.json(dbModel)
})
.catch(function(err) {
res.json(err);
});
}
};
I have two functions because I tried something a little different for each with the same result. Once it gets to this point, I get the following error ('hit controller' is just the console.log in the create function):
'hit controller'
TypeError: Cannot read property 'create' of undefined
I also get the following error in my console on the browser: xhr.js:178 POST http://localhost:3000/listing/ 500 (Internal Server Error) err from saveListing Error: Request failed with status code 500 at createError (createError.js:16) at settle (settle.js:18) at XMLHttpRequest.handleLoad (xhr.js:77)
Which is coming from my addListing page:
handleFormSubmit = (event) => {
event.preventDefault();
console.log('hit');
console.log("state in handle submit", this.state.title)
if (
this.state.title &&
this.state.description &&
this.state.duration &&
this.state.datesAvailable &&
this.state.tags
) {
API.saveListing({
title: this.state.title,
description: this.state.description,
duration: this.state.duration,
datesAvailable: this.state.datesAvailable,
tags: this.state.tags
})
.then(res => console.log('success'))
.catch((err) => console.log("err from saveListing", err));
}
};
Here is my models file:
var mongoose = require("mongoose");
var Schema = mongoose.Schema;
var ListingSchema = new Schema({
// `title` is of type String
title: {
type: String,
required: true
},
description: {
type: String,
required: true
},
duration: {
type: String,
required: true
},
datesAvailable: {
type: String,
required: true
},
hashtags: {
type: Array,
required: true
}
});
var Listing = mongoose.model("Listing", ListingSchema);
module.exports = Listing;
So re-cap: When the user hit's the submit button, the form runs a function, hit's the API with the route it needs, heads to the sever, then to the route for the post request, then the llistingController for the create function for the post, while pulling from my Listing DB model. Once the create function is reached, it poops out. The data is apparently undefined, but I don't know why and can't figure it out.
I feel like what I'm running into is probably basic as fuck but I can't for the life of me figure it out. Additionally if anyone knows some bad ass resources for adding to my mongo/express/react knowledge i'd love to see it.
You're importing db from the models file, but that file is exporting Listing. Import Listing and use Listing.create instead of db.Listing.create.
As 223seneca said, I changed db.Listing.create in my controller to just Listing.create. Then I required Listing in the controller file to the exact file, instead of to just the models folder('const Listing = require('../database/models/Listing');' instead of 'const Listing = require('../database/models');'.
That was all I needed!

Resources