I'm working on a small todos app with nodejs and mongodb.
I have the model definition here:
const Todo = new Schema({
text: {
type: String,
require: true,
minlength: 5,
trim: true
},
completed: {
type: Boolean
},
createdAt: {
type: {type: Date, default: Date.now}
}
});
As you can see, the text property is required and it should throw an error if it's missing when it reads the request.
Over here, I'm send the data to my endpoint:
app.post('/todos', (req, res) => {
let todo = new Todo({
text: req.body.text,
completed: req.body.completed
});
todo.save()
.then((document) => {
res.send(document);
}, (error) => {
res.status(400).send(error);
})
});
And finally, this is my test for the specific scenario where the user sends a empty set of data to the server:
it('Should not create todo document with invalid body data', (done) => {
request(app)
.post('/todos')
.send({})
.expect(400)
.end((error, res) => {
if(error){
return done(error);
}
Todo.find()
.then((todos) => {
expect(todos.length).toBe(0);
done();
}).catch((error) => done(error));
});
});
After running the test, for some reason it throws the following:
1) POST /todos
Should not create todo document with invalid body data:
Error: expected 400 "Bad Request", got 200 "OK"
at Test._assertStatus (node_modules\supertest\lib\test.js:266:12)
at Test._assertFunction (node_modules\supertest\lib\test.js:281:11)
at Test.assert (node_modules\supertest\lib\test.js:171:18)
at Server.assert (node_modules\supertest\lib\test.js:131:12)
at emitCloseNT (net.js:1689:8)
at process._tickCallback (internal/process/next_tick.js:152:19)
I've been trying to debug this for the past hour and I can't find what's wrong with it. Can anyone give me a hand?
UPDATE
Other test:
it('Should create a new todo', (done) => {
let text = 'This is a string';
request(app)
.post('/todos')
.send({text})
.expect(200)
.expect((res) => {
let testString = res.body.text;
expect(testString).toBe(text);
expect(typeof testString).toBe('string');
expect(testString.length).not.toBe(0);
})
.end((error, res) => {
if(error) {
return done(error);
}
Todo.find()
.then((todos) => {
expect(todos.length).toBe(1);
expect(todos[0].text).toBe(text);
done();
}).catch((error) => done(error));
});
});
You should check if text and completed exist before using them:
app.post('/todos', (req, res) => {
let text = req.body.text;
let completed = req.body.completed;
if(!completed) { completed = false; }
if(!text) {
res.status(400).send("Request parameters missing");
} else {
let todo = new Todo({
text: req.body.text,
completed: req.body.completed
});
todo.save()
.then((document) => {
res.send(document);
}, (error) => {
res.status(400).send(error);
})
}
});
Also in your Schema it should be "required" instead of "require"
Related
hope you spent really good holidays
I'm following a tutorial online but i've an error that the teacher doesn't have in the video (i also copy his code but same error).
I have this error only with the "PUT" function but the strange thing is that it updates well in mongoDB. It just gives me an error.
Here's my code:
user.controller.js
const UserModel = require("../models/user.model");
const ObjectID = require("mongoose").Types.ObjectId;
module.exports.getAllUsers = async (req, res) => {
const users = await UserModel.find().select("-password");
res.status(200).json(users);
};
module.exports.userInfo = (req, res) => {
if (!ObjectID.isValid(req.params.id))
return res.status(400).send("ID unknown : " + req.params.id);
UserModel.findById(req.params.id, (err, docs) => {
if (!err) res.send(docs);
else console.log("Id unknown" + err);
}).select("-password");
};
module.exports.updateUser = async (req, res) => {
if (!ObjectID.isValid(req.params.id))
return res.status(400).send("ID unknown : " + req.params.id);
try {
await UserModel.findOneAndUpdate(
{ _id: req.params.id },
{
$set: {
bio: req.body.bio,
},
},
{ new: true, upsert: true, setDefaultsOnInsert: true },
(err, docs) => {
if (!err) return res.send(docs);
if (err) return res.status(500).send({ message: err });
}
);
} catch (err) {
return res.status(500).json({ message: err });
}
};
module.exports.deleteUser = async (req, res) => {
if (!ObjectID.isValid(req.params.id))
return res.status(400).send("ID unknown : " + req.params.id);
try {
await UserModel.remove({ _id: req.params.id }).exec();
res.status(200).json({ message: "Successfully deleted. " });
} catch (err) {
return res.status(500).json({ message: err });
}
};
user.model.js :
const mongoose = require("mongoose");
const { isEmail } = require("validator");
const bcrypt = require("bcrypt");
const userSchema = new mongoose.Schema(
{
pseudo: {
type: String,
required: true,
minlength: 3,
maxlength: 55,
unique: true,
trim: true,
},
email: {
type: String,
required: true,
validate: [isEmail],
lowercase: true,
trim: true,
},
password: {
type: String,
required: true,
max: 1024,
minlength: 6,
},
picture: {
type: String,
default: "./upload/profil/random-user.png",
},
bio: {
type: String,
max: 1024,
},
followers: {
type: [String],
},
following: {
type: [String],
},
likes: {
type: [String],
},
},
{
timestamps: true,
}
);
// playfunction before save into db
userSchema.pre("save", async function (next) {
const salt = await bcrypt.genSalt();
this.password = await bcrypt.hash(this.password, salt);
next();
});
// Export user
const UserModel = mongoose.model("user", userSchema);
module.exports = UserModel;
Here the error in terminal:
(node:16752) [MONGODB DRIVER] Warning: collection.remove is deprecated. Use deleteOne, deleteMany, or bulkWrite instead. (Use `node --trace-warnings ...` to show where the warning was created) events.js:292
throw er; // Unhandled 'error' event
^
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
at ServerResponse.setHeader (_http_outgoing.js:558:11)
at ServerResponse.header (D:\Programmation\mern\node_modules\express\lib\response.js:771:10)
at ServerResponse.send (D:\Programmation\mern\node_modules\express\lib\response.js:170:12)
at ServerResponse.json (D:\Programmation\mern\node_modules\express\lib\response.js:267:15)
at ServerResponse.send (D:\Programmation\mern\node_modules\express\lib\response.js:158:21)
at D:\Programmation\mern\controllers\user.controller.js:33:30
at D:\Programmation\mern\node_modules\mongoose\lib\model.js:4912:18
at processTicksAndRejections (internal/process/task_queues.js:75:11) Emitted 'error' event on Function instance at:
at D:\Programmation\mern\node_modules\mongoose\lib\model.js:4914:15
at processTicksAndRejections (internal/process/task_queues.js:75:11) { code: 'ERR_HTTP_HEADERS_SENT' }
And here the error in PostMan:
(for information, if i put a wrong id in postman i've my error as expected "ID unknown:" ...)
{
"message": {
"originalStack": "Error\n at model.Query._wrappedThunk [as _findOneAndUpdate] (D:\\Programmation\\mern\\node_modules\\mongoose\\lib\\helpers\\query\\wrapThunk.js:25:28)\n at D:\\Programmation\\mern\\node_modules\\kareem\\index.js:279:20\n at _next (D:\\Programmation\\mern\\node_modules\\kareem\\index.js:103:16)\n at D:\\Programmation\\mern\\node_modules\\kareem\\index.js:508:38\n at processTicksAndRejections (internal/process/task_queues.js:75:11)"
}
}
Earlier i change :
catch (err) {
return res.status(500).json({ message: err });
}"
to a simple
console.log(err)
And had this error in the terminal(but not in Postman anymore):
MongooseError: Query was already executed: user.findOneAndUpdate({ _id: new ObjectId("61363178c0b345e93...
at model.Query._wrappedThunk [as _findOneAndUpdate] (D:\Programmation\mern\node_modules\mongoose\lib\helpers\query\wrapThunk.js:21:19)
at D:\Programmation\mern\node_modules\kareem\index.js:279:20
at _next (D:\Programmation\mern\node_modules\kareem\index.js:103:16)
at D:\Programmation\mern\node_modules\kareem\index.js:508:38
at processTicksAndRejections (internal/process/task_queues.js:75:11) {
originalStack: 'Error\n' +
' at model.Query._wrappedThunk [as _findOneAndUpdate] (D:\\Programmation\\mern\\node_modules\\mongoose\\lib\\helpers\\query\\wrapThunk.js:25:28)\n' +
' at D:\\Programmation\\mern\\node_modules\\kareem\\index.js:279:20\n' +
' at _next (D:\\Programmation\\mern\\node_modules\\kareem\\index.js:103:16)\n' +
' at D:\\Programmation\\mern\\node_modules\\kareem\\index.js:508:38\n' +
' at processTicksAndRejections (internal/process/task_queues.js:75:11)'
}
Thank you in advance for your help !
Have a good day :)
EDIT: After the 1st answer from mohammad Naimi:
I add else, and i still have the same issue, mongodb is actually update but i've the error message
here's my updated code
const UserModel = require("../models/user.model");
const ObjectID = require("mongoose").Types.ObjectId;
module.exports.getAllUsers = async (req, res) => {
const users = await UserModel.find().select("-password");
res.status(200).json(users);
};
module.exports.userInfo = (req, res) => {
if (!ObjectID.isValid(req.params.id))
return res.status(400).send("ID unknown : " + req.params.id);
else {
UserModel.findById(req.params.id, (error, docs) => {
if (!error) res.send(docs);
else console.log("Id unknown" + error);
}).select("-password");
}
};
module.exports.updateUser = async (req, res) => {
if (!ObjectID.isValid(req.params.id))
return res.status(400).send("ID unknown : " + req.params.id);
else {
try {
await UserModel.findOneAndUpdate(
{ _id: req.params.id },
{
$set: {
bio: req.body.bio,
},
},
{ new: true, upsert: true, setDefaultsOnInsert: true },
(error, docs) => {
if (!error) return res.send(docs);
else {
return res.status(500).send({ message: error });
}
}
);
} catch (error) {
return res.status(500).json({ message: error });
}
}
};
module.exports.deleteUser = async (req, res) => {
if (!ObjectID.isValid(req.params.id))
return res.status(400).send("ID unknown : " + req.params.id);
else {
try {
await UserModel.remove({ _id: req.params.id }).exec();
res.status(200).json({ message: "Successfully deleted. " });
} catch (error) {
return res.status(500).json({ message: error });
}
}
};
1st solution: Downgrade your Mongoose version (to 5.10.13 for exemple)
2nd solution: Don't use the callback function, use a .then instead (with Mongoose 6.0.13) :
module.exports.updateUser = async (req, res) => {
if (!ObjectID.isValid(req.params.id))
return res.status(400).send("ID invalid : " + req.params.id);
try {
await UserModel.findOneAndUpdate(
{ _id: req.params.id },
{
$set: {
bio: req.body.bio,
},
},
{ new: true, upsert: true, setDefaultsOnInsert: true }
)
.then((docs) => res.send(docs))
.catch((err) => res.status(500).send({ message: err }));
} catch (err) {
res.status(500).json({ message: err });
}
};
Ok, so after multiple try and fails ! here's the answer : Use mongoose#5.10.6 and it should be good
Have an awesome day coder buddies
I also experienced the same problem but what I erased the callback functions and instead I used the (then) instead of the callback functions
exports.likePost = async (req, res) => {
if (!objectID.isValid(req.params.id))
return res.status(400).send("ID unkonwn:" + req.params.id);
try {
// update for collection post
await postModel.findByIdAndUpdate(
req.params.id,
{
$addToSet: { likers: req.body.id }
},
{ new: true }
// update for colletcion users
).then(async (resp) => {
await userModel.findOneAndUpdate(
req.body.id,
{ $addToSet: { likes: req.params.id } },
{ new: true }
).then(data => {
return res.status(201).json({ message: 'inserted with success' })
});
});
} catch (error) {
return res.status(400).send(error)
}
}
Ok, so after multiple try and fails ! here's the answer : Use mongoose#5.10.6 and it should be good
Have an awesome day coder buddies
Use "mongoose#5.10.6" and your programm will work
I also had the same error but after several searches, I found that instead of a callback function you have to use
.then((docs) => res.send(docs))
In my social media application, when a user comments on a post, it throws a 500 Internal Server Error.
The console states POST https://shielded-journey-88539.herokuapp.com/https://us-central1-myapp-1d191.cloudfunctions.net/api/post/3Y7OcHJXXXa0ilBeq35u/comment 500 (Internal Server Error)
When I check the commentOnPost route on Postman, the response returns a Status 200, but the body returns Invalid Host Header.
// Comment on a Post API
exports.commentOnPost = (req, res) => {
if (req.body.body.trim() === '') {
return res.status(400).json({ comment: 'Cannot be empty' });
}
const newComment = {
body: req.body.body,
createdAt: new Date().toISOString(),
postId: req.params.postId,
userHandle: req.user.handle,
profileImage: req.user.profileImage
};
db.doc(`/posts/${req.params.postId}`)
.get()
.then(doc => {
if (!doc.exists) {
return res.status(404).json({ error: 'Post does not exist.' });
}
// after gaining access to document, use prefix reference to update comment count
return doc.ref.update({ commentCount: doc.data().commentCount + 1 })
})
.then(() => { // add newComment to comments collection
return db.collection('comments').add(newComment);
})
.then(() => {
res.json(newComment);
})
.catch(err => {
console.log(err);
res.status(500).json({ error: 'Something went wrong' });
});
};
When I console.log(commentData) inside of dataSlice/submitComment, it returns just the req.body.body and not the rest of the newComment object, from the commentOnPost route.
// submitComment of dataSlice
export const submitComment = (postId, commentData) => dispatch => {
console.log(commentData)
return axios
.post(`/post/${postId}/comment`, commentData)
.then(res => {
dispatch(submitTheComment(res.data))
dispatch(clearErrors());
})
.catch(err => dispatch(setErrors(err.response)))
};
I'm using my own Heroku proxy server.
// App.jsx
axios.defaults.baseURL =
'https://shielded-journey-88539.herokuapp.com/https://us-central1-myapp-1d191.cloudfunctions.net/api';
// package.json
"proxy": "https://shielded-journey-88539.herokuapp.com/https://us-central1-myapp-1d191.cloudfunctions.net/api"
What am I doing wrong?
Can you try this code, and console.log(commentData), and where is commentData?
exports.commentOnPost = (req, res) => {
if (req.body.body.trim() === '') {
return res.status(400).json({ comment: 'Cannot be empty' });
}
const newComment = {
body: req.body.body,
createdAt: new Date().toISOString(),
postId: req.params.postId,
userHandle: req.user.handle,
profileImage: req.user.profileImage
};
console.log("newComment: ", newComment)
db.doc(`/posts/${req.params.postId}`).get()
.then(doc => {
if (!doc.exists) {
return res.status(404).json({ error: 'Post does not exist.' });
}
// after gaining access to document, use prefix reference to update comment count
return doc.ref.update({ commentCount: doc.data().commentCount + 1 });
}).then(() => {
// add newComment to comments collection
db.collection('comments').add(newComment);
res.status(200).json(newComment);
}).catch(err => {
console.log("Error in Catch commentOnPost: ", err);
res.status(500).json({ error: 'Something went wrong' });
});
};
I have created a delete account route to delete an users information after certain days. I had used SetTimeOut() function to achieve this. But the problem is if in case for some reason the server gets restarted this does not works. So, is there any other method to achieve this.
Thank you.
Below is the code,
router.post("/delete", async (req, res, next) => {
const mailId = req.body.email;
const deletionTime = moment().format("DD-MMM-YYYY, h:mm:ss a");
await Register.findOne({ email: mailId })
.exec()
.then(async (result) => {
if (req.body.password === result.password) {
await Register.updateOne(
{ email: mailId },
{
$set: {
accountDeleted: true,
deletionFeedback: req.body.deletionFeedback,
latitude: req.body.latitude,
longitude: req.body.longitude,
deletionDate: deletionTime,
deletionIP: req.body.deletionIP,
isp: req.body.isp,
location: req.body.location,
},
}
)
.exec()
.then(async (result) => {
setTimeout(async function(){
await Register.updateOne(
{ email: mailId },
{
$set: {
permanentlyDeleted: true,
},
}
)
.exec();
}, 60000);
res.status(200).json({
userDeleted: true,
});
})
.catch((err) => {
console.log(err);
res.status(400).json({
userDeleted: false,
});
});
} else {
res.status(400).json({
result: 0,
message: "Invalid password.",
});
}
})
.catch((err) => {
res.status(400).json({
result: 0,
message: "Invalid Email ID.",
});
});
});
The thing is if I restart the server the function inside setTimeOut() method doesn't work. Is there any alternative for this.
I'm trying to add comments functionality into my Sails.js blog application. However, I don't seem to write my controller action correctly.
When I submit the comment form, the page starts to reload, but does not finish reloading.
Here's my controller code:
const gravatar = require('gravatar');
module.exports = {
blog: (req, res) => {
Post.find({}).exec((err, posts) => {
if (err) {
res.send(500, { error: 'Database Error' });
}
res.view('all-posts', { posts });
});
},
singlePost: (req, res) => {
Post.findOneBySlug(req.params.slug).exec((err, post) => {
if (err) {
res.send(500, { error: 'Database Error' });
}
res.view('single-post', {
post,
gravatar: gravatar.url
});
});
},
addComment: (req, res) => {
const {
name, comment, email,
url, slug,
} = req.allParams();
Post.findOneBySlug(slug).exec((err, post) => {
if (err) {
return res.send(500, { error: 'Database Error' });
Comment.create({
body: comment, name, email, website: url
}).exec((error, comment) => {
if (error) {
return res.send(500, { error: 'Database Error' });
}
console.log(comment);
post.comments.addComment({slug, comment});
post.save();
res.redirect(`/${slug}`);
});
}
});
return false;
},
};
And here's my routes.js file:
module.exports.routes = {
'get /blog': 'BlogController.blog',
'get /:slug': 'BlogController.singlePost',
'post /:slug/new-comment': 'BlogController.addComment'
};
And this is my model Post.js
module.exports = {
identity: 'Post',
attributes: {
title: {
type: 'string',
required: true,
unique: true
},
body: {
type: 'string'
},
categories: {
type: 'string',
required: true
},
imageUrl: {
type: 'string'
},
comments: {
collection: 'Comment',
via: 'post'
},
slug: {
type: 'slug',
from: 'title',
blacklist: ['search', 'blog', 'contacts']
}
},
addComment: (options, cb) => {
Post.findOneBySlug(options.slug).exec((err, post) => {
if (err) return cb(err);
if (!post) return cb(new Error('Post not found.'));
post.comments.add(options.comment);
post.save(cb);
})
},
connection: 'mongodb'
};
So, when I submit the comment form on the /:slug page, nothing actually happens accept the page tries to reload. And in the database nothing gets saved as well.
The form parameters get sent from the form, so on the client side everything should be fine.
How how I approach this post request correctly?
You need to add return statement before each res.send(500, ...); call, because currently, in the case of the error, your code tries to send the response twice, and client doesn't get the response with the actual error:
if (err) {
return res.send(500, { error: 'Database Error' });
}
... rest code
I suspect, that the reason why nothing is saved in db is invalid parameters in request body.
upsertAsync: function(req, res, next) {
var datas = req.body;
var user = req.body.user;
async.each(datas, function(data, cb) {
sequelize().transaction({
autocommit: true
}, function(t) {
models.Customer.upsert({
id: data.id,
code: data.code,
createdBy: user.name,
modifiedBy: user.name
}, {
transaction: t
}).then(function(customer) {
cb();
}).
catch (function(error) {
t.rollback();
res.status(500).json(error);
});
}, function(err, data) {
log.debug('error (upsertAsync)', err);
if (err) {
t.rollback();
res.status(500).json(err);
}
t.commit();
res.json({
responseCode: '200',
message: 'Customer has been created..!',
data: data
});
});
});
},
I'm using async.each to insert data into sqlite database at the same time. I want to rollback if any error occurs but it shows error which is [TypeError: Cannot set property 'options' of undefined