node Js : API post 400 bad request - node.js

this is my first project in node js and i created an API that have get, delete, update, and post.
i tested GET and response 200 ok but POST is 400 bad request i don't know why ...
i tested this in postman and vue js too but same result
below is my code in nodeJs:
can someone please tell me what is the problem?
thanks in advance
//question.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const questionSchema = new Schema({
questionTitle: {
type: String,
required: true,
},
is_solver_selected : {
type: Boolean,
default : false,
},
reviewed: {
type: Boolean,
default : false,
},
question_file_path: {
type: String,
required: true
},
description : {
type : String,
required : true
},
solutions : [
{
type: Schema.Types.ObjectId,
ref:"Solution",
default : []
}
],
owner : {
type : Schema.Types.ObjectId,
ref : "User",
required : true
},
categories : [{
type : Schema.Types.ObjectId,
ref : "Category",
required : true
}],
answered: {
type: Boolean,
default : false,
},
budget : {
type : String,
required : true
}
}, { timestamps: true });
const Question = mongoose.model('Question', questionSchema);
module.exports = Question;
//questionApiController
const Category = require('../models/category');
const Question = require('../models/question');
const User = require('../models/user');
const validators = require('../validators');
let questionApiController = {
// Get a single question
get_question : async (req , res) => {
const id = req.params.id;
try {
const question = await Question.findById(id,(err, question) => {
if (err) return res.status(400).json({response : err});
res.status(200).json({response : question})
}).populate('owner', 'username').populate('categories').populate({
path : "solutions",
populate : {path : "solver_candidate" , select : "username"}});
} catch (err) {
res.status(400).json(err);
}
},
// Get all the questions
get_questions: async (req , res) => {
try {
const questions = await Question.find((err, questions) => {
if (err) return res.status(400).json({response : err});
res.status(200).json({response : questions})
}).sort({ createdAt: -1 }).populate('owner', 'username').populate('categories').populate({
path : "solutions",
populate : {path : "solver_candidate" , select : "username"}});
} catch (err) {
res.status(400).json(err);
}
},
// Create a question
create_question : async (req , res) => {
const {error} = validators.postQuestionValidation(req.body);
if(error) return res.status(400).json({ "response" : error.details[0].message})
try {
const question = await new Question(req.body);
User.findByIdAndUpdate(req.body.owner)
.then(result => {
result.questions.push(question._id)
result.save((err, categories) => {
if (err) return res.status(400).json({response : {error : err , explanation : " Error binding to the user !"}});
});
})
.catch(err => {
res.status(400).json({response: err });
});
req.body.categories.forEach(element => {
Category.findById(element).then(result => {
}).catch(err => {
res.status(400).json({response : err });
})
})
question.save((err, question) => {
if (err) return res.status(400).json({response : err});
res.status(200).json({response : " Question created Successfully"})
});
} catch (err) {
res.status(400).json(err);
}
},
// Delete question
delete_question : async (req , res) => {
const id = req.params.id;
var questionExist = false;
var userId ;
const question = await Question.findById(id).then(result => {
questionExist = true;
userId = result.owner;
}).catch(err => {
questionExist = false;
res.status(400).json({response : err });
});
if(questionExist){
try {
Question.findByIdAndRemove(id ,(err, question) => {
// As always, handle any potential errors:
if (err) return res.json({response : err});
// We'll create a simple object to send back with a message and the id of the document that was removed
// You can really do this however you want, though.
const response = {
message: "Question successfully deleted",
id: question._id
};
return res.status(200).json({response : response });
});
User.findByIdAndUpdate(userId)
.then(result => {
let pos = result.questions.indexOf(question._id);
result.questions.splice(pos,1);
result.save((err, categories) => {
if (err) return res.status(400).json({response : {error : err , explanation : "Error binding unbinding from the user"}});
});
})
.catch(err => {
res.json({response: err });
});
} catch (err) {
res.status(400).json(err);
}
}
else {
return res.status(400).send( { "response" : "A question with that id was not find."});
}
},
// Update question
update_question : async (req , res) => {
const id = req.params.id;
Question.findByIdAndUpdate(id,req.body,
function(err, result) {
if (err) {
res.status(400).json({response : err});
} else {
res.status(200).json({response : "Question Updated"});
console.log(result);
}
})
},
bind_question_to_solver : async (req , res) => {
const id = req.params.id;
Question.findByIdAndUpdate(id,{
solver : req.body.solver ,
response_file_path : req.body.response_file_path},function(err, result) {
if (err) {
res.status(400).json({response : err});
} else {
res.status(200).json({response : "Question Bind to Solver"});
//Update the user solver by adding the question id in its quesion array1
console.log(result);
}
})
},
// Get question's questions
get_question_categories : async (req , res) => {
const id = req.params.id;
try {
const question_categories = await Question.findById(id).populate("categories")
console.log(question_categories)
res.json(question_categories)
} catch (err) {
console.log(err);
res.json(err);
}
}
}
module.exports = questionApiController
//question controller
const Question = require('../models/question');
const question_index = (req, res) => {
Question.find().sort({ createdAt: -1 })
.then(result => {
res.render('index', { questions: result, title: 'All questions' });
})
.catch(err => {
console.log(err);
});
}
const question_details = (req, res) => {
const id = req.params.id;
Question.findById(id)
.then(result => {
res.render('details', { question: result, title: 'Question Details' });
})
.catch(err => {
console.log(err);
res.render('404', { title: 'Question not found' });
});
}
const question_create_get = (req, res) => {
res.render('create', { title: 'Create a new question' });
}
const question_create_post = (req, res) => {
const question = new Question(req.body);
question.save()
.then(result => {
res.redirect('/questions');
})
.catch(err => {
console.log(err);
});
}
const question_delete = (req, res) => {
const id = req.params.id;
Question.findByIdAndDelete(id)
.then(result => {
res.json({ redirect: '/questions' });
})
.catch(err => {
console.log(err);
});
}
module.exports = {
question_index,
question_details,
question_create_get,
question_create_post,
question_delete
}
sent request:
Post http://localhost:9000/questions/api/add
content-type: application/json
{
"description": "d",
"questionTitle": "ddd",
"categories":"ddd",
"question_file_path":"d.pdf",
"budget":"d",
"owner":"bla",
}
`error message': HTTP/1.1 400 Bad Request
SyntaxError: Unexpected token } in JSON at position 160
at JSON.parse ()
at parse (C:\Users\saad\Desktop\APi-master-nodejs\node_modules\body-parser\lib\types\json.js:89:19)
at C:\Users\saad\Desktop\APi-master-nodejs\node_modules\body-parser\lib\read.js:121:18
at invokeCallback (C:\Users\saad\Desktop\APi-master-nodejs\node_modules\raw-body\index.js:224:16)
at done (C:\Users\saad\Desktop\APi-master-nodejs\node_modules\raw-body\index.js:213:7)
at IncomingMessage.onEnd (C:\Users\saad\Desktop\APi-master-nodejs\node_modules\raw-body\index.js:273:7)
at IncomingMessage.emit (events.js:327:22)
at endReadableNT (_stream_readable.js:1327:12)
at processTicksAndRejections (internal/process/task_queues.js:80:21)
update:
tested on vue js app console logs
enter image description here

based on your updated post, i would start to fix the provided json. because as the response says, it is invalid. JSON does not have trailing comma at the end of the last entry.
{ "description": "d", "questionTitle": "ddd", "categories":"ddd", "question_file_path":"d.pdf", "budget":"d", "owner":"bla"}
would be fine.

Check why-body-parser-json-is-not-working-showing-syntaxerror-unexpected-token post, you have a similar problem.
You are sending , symbol after last field in the json object which is not expected by the body-parser when you are sending content-type: application/json

In questionSchema you have a field called categories which must be an array of ObjectIds but you are sending a string in the request body
categories : [{
type : Schema.Types.ObjectId,
ref : "Category",
required : true
}],

Related

how do i use find() with mongoose to find by another field

I am trying to find subscriptions by MovieId,or MembeId
this is the schema:
const SubscriptionSchema = new mongoose.Schema({
MovieId : String,
MemberId : String,
Date : Date
})
this is the server:
BL:
const getAllSubscriptionsByMovieId = function(movieid)
{
return new Promise((resolve,reject) =>
{
Subscription.find({MovieId : movieid}, function(err, data)
{
if(err)
{
reject(err)
}
else
{
resolve(data);
}
})
})
}
router:
router.route('/')
.get( async function(req,resp)
{
let data = await subscriptionsBL.getAllSubscriptionsByMovieId(movieid)
return resp.json(data);
})
what is wrong??

findByIdAndUpdate result in an "validation failed" error when i set "runValidators" to "true"

i have an express app with a put request that updates a phonebook if i found that the person's name already exists in this phone book (i'm using the "mongoose-unique-validator" that have the unique: true option in validation)
but i have the problem with this put request only when i set findByIdAndUpdate's runValidators to true
here is my code
the schema
const personSchema = new mongoose.Schema({
name: { type: String, required: true, minlength: 3, unique: true },
number: {
type: String,
required: true,
validate: {
validator: function (str) {
//the function to validate the number
},
message: "phone number must contain at least 8 digits",
},
},
});
personSchema.plugin(uniqueValidator);
personSchema.set("toJSON", {
transform: (document, returnedObject) => {
returnedObject.id = returnedObject._id.toString();
delete returnedObject._id;
delete returnedObject.__v;
},
});
const Person = mongoose.model("Person", personSchema);
the put request
app.put("/api/persons/:id", (req, res, next) => {
const id = req.params.id;
console.log(id);
const body = req.body;
const person = {
name: body.name,
number: body.number,
};
// opts is supposed to be true; and this is where i have the problem
const opts = { runValidators: false };
Person.findByIdAndUpdate(id, person, opts)
.then((updatedPerson) => {
res.json(updatedPerson);
})
.catch((error) => next(error));
});
the error handler
const errorHandler = (error, req, res, next) => {
console.error(error.message);
if (error.name === "CastError" && error.kind == "ObjectId") {
return res.status(400).send({ error: "malformatted id" });
} else if (error.name === "ValidationError") {
return res.status(400).json({ error: error.message });
}
next(error);
};
app.use(errorHandler);
the error i'm getting is
error: "Validation failed: name: Cannot read property 'ownerDocument' of null"
#oussama ghrib If you're not updating the name and you're only updating the number (as you indicated), the update object can just have the phone number, like below:
app.put('/api/persons/:id', (req, res, next) => {
const { number } = req.body
Person.findByIdAndUpdate(req.params.id, { number }, {
new: true,
runValidators: true,
context: 'query'
})
.then((updatedPeep) => {
res.json(updatedPeep)
})
.catch((err) => next(err))
})
If you're using runValidators: true you also need context: 'query' as explained here: https://github.com/blakehaswell/mongoose-unique-validator#find--updates
findOneAndUpdate works like findByIdAndUpdate for this purpose.
The issue is with your usage of: findByIdAndUpdate(id, update, opts)
The update object should be properties you want to update otherwise it will also to try update the id.
Solution:
app.put("/api/persons/:id", (req, res, next) => {
const { name, number } = req.body;
Person.findByIdAndUpdate(req.params.id, { name, number }, opts )
.then((updatedPerson) => {
res.json(updatedPerson);
})
.catch((err) => next(err));
}
https://mongoosejs.com/docs/api/model.html#model_Model.findByIdAndUpdate

findOneAndUpdate seems to work Robo 3T but POST request results in 'Pending' through Axios

I'm a bit stumped and was wondering if anyone could help. Whenever I call an axios post, the network tab shows that the request is pending and ultimately fails. When I try the same call through Robo 3T, it updates succesfully.
Can anyone give me some insight? Thank you!
Here's the route I'm using:
router.post('/upvote/reply/id/:id',
// passport.authenticate('jwt', { session: false }),
async (req, res) => {
await Posts.findOneAndUpdate(
{ "comments._id": mongoose.Types.ObjectId(req.params.id) },
{
$inc: { "comments.$.points": 1 },
$push: { "comments.$.upvotedBy": req.user._id },
$pull: { "comments.$.downvotedBy": req.user._id },
},
(err, result) => {
if (err) {
return res.status(404).json({
success: false,
error: err,
message: 'Post not upvoted!',
})
}
else {
return res.status(200).json({
success: true,
data: result
})
}
})
.catch(err => console.log(err))
})
Here's how I'm calling my API route:
handleReplyUpvote = (id) => {
this.setState(prevState => {
const updatedReplies = prevState.replies.map(item => {
if (item._id === id) {
try {
axios
.post(`http://localhost:5000/api/posts/upvote/reply/id/${id}`)
.then(res => {
// console.log(res.data.data[0].comments[0])
console.log(res)
// ...item,
// const {posts} = this.state
// posts.push(res.data)
// this.setState({posts})
})
}
catch (err) {
console.log(err)
}
return {
...item,
// voted: true,
points: item.points + 1
}
}
return item
})
return {
replies: updatedReplies
}
})
// console.log('boops')
}
A little more context code which might help:
const replies = this.state.replies.slice().map((item, i) =>
<div
key={i}
className='replyItem'
>
<Reply
// key={i}
reply={item.reply}
id={item._id}
user_id={item.user_id}
createdAt={item.createdAt}
points={item.points}
handleDelete={() => this.handleDelete(item._id)}
user={this.props.auth}
handleReplyUpvote={() => this.handleReplyUpvote(item._id)}
// handleDownvote={() => this.handleReplyDownvote(item._id.points)}
/>
</div>
)
You are mixing async/await, promises and callbacks. Use either promises or asyns/await, not all. I have fixed few things and it should work. (I didn't test it though)
router.post("/upvote/reply/id/:id", async (req, res) => {
try {
const result = await Posts.findOneAndUpdate(
{ "comments._id": mongoose.Types.ObjectId(req.params.id) },
{
$inc: { "comments.$.points": 1 },
$push: { "comments.$.upvotedBy": req.user._id },
$pull: { "comments.$.downvotedBy": req.user._id },
}
);
return res.status(200).json({
success: true,
data: result,
});
} catch (error) {
return res.status(404).json({
success: false,
error: error.message,
message: "Post not upvoted!",
});
}
});
handleReplyUpvote = async(id) => {
const updatedReplies = [];
for(const item of this.state.replies){
if(item._id === id){
try{
const response = await axios
.post(`http://localhost:5000/api/posts/upvote/reply/id/${id}`)
console.log(response.data);
}catch(error){
console.log(error.message);
}
updatedReplies.push({
...item,
points: item.points + 1;
})
continue;
}
updatedReplies.push(item);
}
this.setState({
replies: updatedReplies
})
}

Get data from different model nodejs mongodb express js

I have two model files Bank Deposits and Sub account details. From Sub account details I want to get current balance to Bank deposits . I want to do this in mongodb and node js. I am currently aggreagate lookup operation but it is showing the array as empty.
BankdepositModel.js
var mongoose = require("mongoose");
var BankDepositsSchema = new mongoose.Schema(
{
source_sub_account: Array,
amount: Number,
cheque_notes: Number,
to_bank_account: Array,
amount_in_words: String,
bank_Ref_no: String
},
{
timestamps: true
}
);
module.exports = mongoose.model("BankDeposits", BankDepositsSchema);
Subaccountdetailsmodel.js
var mongoose = require("mongoose");
var SubAccountDetailsSchema = new mongoose.Schema(
{
sub_account_name: String,
current_balance: Number,
account: Array
},
{
timestamps: true
}
);
module.exports = mongoose.model("SubAccountDetails", SubAccountDetailsSchema);
Controller.js
var BankDeposits = require("../model/bankdepositmodel");
var SubAccountDetails = require("../model/subaccountsmodel.js");
exports.create1 = (req, res) => {
var BankDeposit = new BankDeposits({
source_sub_account: req.body.source_sub_account,
amount: req.body.amount,
cheque_notes: req.body.cheque_notes,
to_bank_account: req.body.to_bank_account,
amount_in_words: req.body.amount_in_words,
bank_ref_no: req.body.bank_ref_no
});
BankDeposit.save()
.then(data1 => {
res.send(data1);
})
.catch(err => {
res.status(500).send({
message: err.message
});
});
};
//BankDeposit get
exports.find1 = (req, res) => {
BankDeposits.aggregate([
{
$lookup: {
from: "SubAccountDetails",
localField: "current_balance",
foreignField: "current_balance",
as: "balance"
}
}
])
.then(appdata => {
res.status(200).send(appdata); //On successful fetch, server responds with status 200
})
.catch(err => {
res.status(400).send(err); //On error, server responds with status 400
});
};
//sub account details post
exports.createSubAccountDetail = (req, res) => {
var SubAccountDetail = new SubAccountDetails({
sub_account_name: req.body.sub_account_name,
current_balance: req.body.current_balance,
account: req.body.account
});
SubAccountDetail.save()
.then(SubAccountDetail => {
res.send(SubAccountDetail);
})
.catch(err => {
res.status(500).send({
message: err.message
});
});
};
//sub account details get
exports.SubAccountDetail = (req, res) => {
SubAccountDetails.find()
.then(SubAccountDetails => {
res.send(SubAccountDetails);
})
.catch(err => {
res.status(500).send({
message: err.message || "Some error occurred while retrieving regs."
});
});
};
You can get it like this
BankDeposits.aggregate([
{
$lookup: {
from: "SubAccountDetails",
localField: "source_sub_account",
foreignField: "sub_account_name",
as: "sub_acount"
}
}
])
Now you will get your complete sub_account object in sub_account property of returned data, current_balance will be in same
This is assuming that sub_account_name in SubAccountDetails & source_sub_account in BankDeposits are same

Mongo/Mongoose field value aren't updated

I have a problem with my MongoDB collection. If i try to replace field in my item it won't get saved. It logs new value correctly, but nothing in DB really changes. What's wrong here?
exports.update = function (req, res) {
const { entryid, userid } = req.params;
let field;
['stop', 'description'].forEach(item => req.query[item] ? field = item : -1);
db.TimeEntry.findById(entryid).then(function (entry) {
(req.query[field] === undefined) ? entry[field] = 'no value specified' : entry[field] = req.query[field];
console.log('v:', entry[field]);
entry.save(function (err) {
if (err) console.log(err);
db.TimeEntry.find({ userId: userid })
.then(foundEntries => res.status(200).json(foundEntries));
});
}).catch(err => console.log(err));
}
Schema:
const mongoose = require('mongoose');
const TimeEntrySchema = new mongoose.Schema({
start: {
type: mongoose.Schema.Types.Mixed,
default: Date.now,
required: true
},
stop: mongoose.Schema.Types.Mixed,
description: String,
project: String,
billable: Boolean,
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
}
}, { timestamps: true })
TimeEntrySchema.pre('remove', function (next) {
User.findById(this.userId).then(user => {
user.entries.remove(this.id);
user.save().then(function (e) {
next();
}).catch(err => console.log(err));
})
});
const TimeEntry = mongoose.model('TimeEntry', TimeEntrySchema);
module.exports = TimeEntry;
Edit: Modified this way, still no effect.
` entry[field] = (!req.query[field]) ? 'TESTINGTESTING' : req.query[field];
entry.markModified('description');
console.log('v:', entry[field]);
entry.save().then(function (err) {`
Final edit: Ok so this is the final solution.
exports.update = function (req, res) {
const { entryid, userid } = req.params;
let field;
['stop', 'description'].forEach(item => req.query[item] ? field = item : -1);
db.TimeEntry.update({ _id: entryid }, {
$set: {
description: req.query[field] || 'no value specified'
}
}).then(function () {
db.TimeEntry.find({ userId: userid })
.then((foundEntries) => res.status(200).json(foundEntries))
}).catch(err => console.log(err));
}
Try using .update:
return db.TimeEntry.update({ _id: entryid }, {
$set: {
[field]: req.query[field] || 'no value specified'
}
}
}).then(() => db.TimeEntry.find({ _id: entryid }))
.then((resultEntries) => res.status(200).json(foundEntries))
Have you tried this: entry.save().exec(callbackFN);
instead of entry.save(callbackFN); ?

Resources