NodeJs/Express/MongoDb - ERR_HTTP_HEADERS_SENT - node.js

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

Related

Can't see where multiple call of res caused the error : Cannot set headers after they are sent to the client

I'm following a tutorial in the net. It's a MERN project with mongo/mongoose. When I have implemented the update function in the controller the following error has occured :
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
I've seen the answers about similar issue where they say it's because there are 2 or multiple calls of res (res.json(), res.send() etc..), but I don't see where must I change this in the following function :
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
}
},
(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 });
}
};
It may be that you've mixed up two different error handling patterns.
You don't need try/catch if you're using built in error handling of findOneAndUpdate()
await UserModel.findOneAndUpdate({
_id: req.params.id
}, {
$set: {
bio: req.body.bio
}
},
(err, docs) => {
if (!err)
return res.send(docs);
if (err)
return res.status(500).send({ message: err });
}
)
and if you are using try/catch, you don't need findOneAndUpdate's error handling:
try {
const user = await UserModel.findOneAndUpdate({
_id: req.params.id
}, {
$set: {
bio: req.body.bio
}
})
return res.send(user)
} catch (err) {
return res.status(500).json({ message: err });
}
Could you please change code like this:
module.exports.updateUser = async(req, res) => {
if (!ObjectID.isValid(req.params.id))
return res.status(400).send("ID unknown : " + req.params.id);
try {
const result = await UserModel.findOneAndUpdate({
_id: req.params.id
}, {
$set: {
bio: req.body.bio
}
});
return res.send(result);
} catch (err) {
return res.status(500).json({ message: err });
}
};

mongoose findOneAndUpdate works but gives an error

I learn mongoose on NodeJS and I wanted to use usefindOneAndUpdate, it works on db I have the updates but the response gives an error and I couldn't find why help me pls.
code:
await userModel.findOneAndUpdate(
{ _id: req.params.id },
{
$set: {
nickName: req.body.nickName,
},
},
{ upsert: true, new: true, setDefaultsOnInsert: true },
(err, docs) => {
if (!err) return res.send(docs);
else return res.status(500).send({ message: err });
}
);
} catch (err) {
return res.status(500).json({ message: err });
}
error:
"originalStack": "Error\n at model.Query._wrappedThunk [as _findOneAndUpdate]
(C:\\Users\\alike\\Documents\\projetsJS\\chatApp-Node-React\\node_modules\\mongoose\\lib\\helpers\\query\\wrapThunk.js:25:28)
at C:\\Users\\alike\\Documents\\projetsJS\\chatApp-Node-React\\node_modules\\kareem\\index.js:279:20
at _next (C:\\Users\\alike\\Documents\\projetsJS\\chatApp-Node-React\\node_modules\\kareem\\index.js:103:16)
at C:\\Users\\alike\\Documents\\projetsJS\\chatApp-Node-React\\node_modules\\kareem\\index.js:508:38
at processTicksAndRejections (internal/process/task_queues.js:77:11)"
Edit: #GulshanAggarwal here is all the file https://i.stack.imgur.com/r4LCj.png

Find if item exist in database

i was following some react/express tutorials.
im confused with code below in contacts.js file
if (!contact) {
return res.status(404).json({ msg: "Contact not found" })
}
Is above code necessary ? or whats is there better way to catch if the contact doesn't exist in mongodb ?
when i do put request from postman with invalid id for example
localhost:44022/api/contacts/<invalid ID>
the execution never reach if (!contact) part. if i console log the catch(err) section i get something below
CastError: Cast to ObjectId failed for value "bcvbxcxc" at path "_id" for model "contact"
at model.Query.exec (/Users/becker/Desktop/RJS/ckeeper/node_modules/mongoose/lib/query.js:4380:21)
at model.Query.Query.then (/Users/becker/Desktop/RJS/ckeeper/node_modules/mongoose/lib/query.js:4472:15)
at runMicrotasks (<anonymous>)
at processTicksAndRejections (internal/process/task_queues.js:97:5) {
messageFormat: undefined,
stringValue: '"bcvbxcxc"',
kind: 'ObjectId',
value: 'bcvbxcxc',
path: '_id',
reason: Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters
at new ObjectID (/Users/becker/Desktop/RJS/ckeeper/node_modules/bson/lib/bson/objectid.js:59:11)
contacts.js
router.put('/:id', auth, async (req, res) => {
const { name, email, phone, type } = req.body;
const contactFields = {};
if (name) contactFields.name = name;
if (email) contactFields.email = email;
if (phone) contactFields.phone = phone
if (type) contactFields.type = type;
try {
let contact = await Contact.findById(req.params.id);
if (!contact) {
return res.status(404).json({ msg: "Contact not found" })
}
//makesure contact belongs to right user
if (contact.user.toString() != req.user.id) {
return res.status(401).json({ msg: 'Not authorized' })
}
contact = await Contact.findByIdAndUpdate(req.params.id, { $set: contactFields }, { new: true })
res.json(contact)
}
catch (err) {
console.log("reached error, contact not found")
console.error(err)
res.status(500).send(err)
}
});
Contacts.js model
const mongoose = require('mongoose');
const ContactSchema = mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'users'
},
name: {
type: String,
required: true
},
email: {
type: String,
required: true,
},
phone: {
type: String
},
type: {
type: String,
default: 'personal'
},
date: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('contact', ContactSchema)
The reason for not executing the if(!contact) condition upon exception is because catch block will be called and further execution in the try block is halted. You should rather wrap each db call in its own try catch block. Something like:
let contact;
try {
contact = await Contact.findById(req.params.id);
} catch(err) {
console.log('Some db operations failed due to reason', err);
return res.status(500).json({ msg: "DB operations failed or whatever message" })
}
if (!contact) {
return res.status(404).json({ msg: "Contact not found" })
}
//makesure contact belongs to right user
if (contact.user.toString() != req.user.id) {
return res.status(401).json({ msg: 'Not authorized' })
}
try {
contact = await Contact.findByIdAndUpdate(req.params.id, { $set: contactFields }, { new: true })
return res.json(contact)
}
catch (err) {
console.log("db update operations failed")
console.error(err)
res.status(500).send(err)
}

How to set TTL for a mongo DB document through a node js route

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 want to use async and upsert along with sequelize transaction

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

Resources