Mongoose post Query Middleware hook trigger twice - node.js

I'm using mongoose Model.findOneAndupdate() to find and update my document and there is a post hook on my model schema for which i'm trying to update another document.
The issue i'm facing is post hook is being triggered twice.
My model:
const mongoose = require('mongoose')
const componentSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
component: {
type: String,
required: true
},
message: {
type: String
},
bodyJson: {
type: mongoose.Schema.Types.Mixed
},
question: {
type: String
}
})
componentSchema.post('findOneAndUpdate', function (result) {
console.log('came here')
})
module.exports = mongoose.model('Component', componentSchema)
In my server log i see that came here logged is twice.
update:
try {
await Component.findOneAndUpdate(query, req.body, { new: true }, function (error, doc) {
if (doc) {
return res.status(200).json({ data: doc })
} else if (error) {
return res.status(400).json({ errors: error.message })
} else res.status(404).json({ errors: 'Not found' })
})
} catch (error) {
logger.error('error while updating order: ' + error)
return res.status(400).json({ errors: error.message })
}
moongoose version i'm using is 5.8.11

You are using both await and callback at the same time. This causes the middleware trigger 2 times. Only one of them must be used.
Use either callback:
Component.findOneAndUpdate(query, req.body, { new: true }, function(
error,
doc
) {
if (err) {
return res.status(400).json({ errors: error.message }); //500 status code may be better
} else {
if (doc) {
return res.status(200).json({ data: doc });
} else {
res.status(404).json({ errors: "Not found" });
}
}
});
Or await:
try {
const doc = await Component.findOneAndUpdate(query, req.body, { new: true });
if (doc) {
return res.status(200).json({ data: doc });
} else {
res.status(404).json({ errors: "Not found" });
}
} catch (error) {
logger.error("error while updating order: " + error);
return res.status(400).json({ errors: error.message });
}

Related

MongoDB saves the necessary data to the collection, but outdated data goes to res()

Everything works fine, but the data I get in res() is one step behind. I rewrote the entire code a hundred times and no longer understand what the problem is
here is part of the code backend on express.js, node.js and mongodb:
export const addToCart = async (req, res) => { try {
const cart = await CartModul.findOne({ user: req.userId });
if (cart) {
const product_id = req.body.product_id;
const item = cart.cartItems.find((c) => c.product_id == product_id);
console.log("item", item);
if (item) {
try {
const cart = await CartModul.findOneAndUpdate(
{ user: req.userId, "cartItems.product_id": product_id },
{
"cartItems.$": {
...req.body,
quantity: item.quantity + req.body.quantity,
totalPrice: item.totalPrice + req.body.price,
},
}
);
if (cart) {
return res.status(200).json({ cart });
}
} catch (error) {
return res.status(400).json({ error });
}
} else {
try {
const cart = await CartModul.findOneAndUpdate(
{ user: req.userId },
{
$push: {
cartItems: req.body,
},
}
);
if (cart) {
return res.status(200).json({ cart });
}
} catch (error) {
return res.status(400).json({ error });
}
}
} else {
try {
const cart = new CartModul({
user: req.userId,
cartItems: req.body,
});
cart.save();
res.json(cart);
} catch (error) {
return res.status(400).json({ error });
}
} } catch (error) {
return res.status(400).json({ error })}};
In the else condition add await. i.e.
let newCart = await cart.save();
res.json(newCart);
Use {new: true) in findOneAndUpdate() and make async in moment with save()

How to Remove more than one documents from mongodb

I am trying to deleteFeature meanwhile i want all the comments related to that feature deleted but i don't know how to do it.
my deleteFeature method -
exports.deleteFeature = (req, res) => {
try {
const { slug } = req.params;
Feature.findOne({ slug: slug.toLowerCase() }).exec((err, feature) => {
if (err) {
return res.status(400).json({
error: errorHandler(err),
});
}
console.log("Test");
Comment.deleteMany({ _id: feature._id });
console.log("chest");
feature.remove();
console.log("Best");
return res.json({
message: "Your Feature has been Deleted Successfully",
});
});
} catch (error) {
return res.status(400).json({
error: error,
});
}
};
I have this on comment model -
feature: {
type: ObjectId,
ref: "Feature",
required: true,
},
So when i delete a feature, i want to delete all the comments containing that feature's _id on that feature field
Change
Comment.deleteMany({ _id: feature._id });
to
Comment.deleteMany({ feature: feature._id });

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

How to update all the data that have same field without modify other data in mongoose?

I have this message collection compose of 4 fields _id, conversationId, message, seen. In my message controller every time the user click specific user the backend will send a list of messages that have same conversationId into the frontend(reactJS). In the frontend that list of messages will be modify by changing the value of seen from false to true. Then I'm planning to pass this to the backend. My problem is how can I modify only all the data that have same conversationId without replacing all of the data inside message collection
Controller that will get all the messages that have same conversationID
export const getMessage = async (req, res) => {
try {
const message = await messageModel
.find({
conversationId: req.params.messageId,
})
.populate('senderId');
res.status(200).json(message);
} catch (error) {
res.status(500).json({ msg: error.message });
}
};
Return Value
[
{
_id: '616d76e858abdc3fa4059ee3',
conversationId: '61696e3ed94cd23f8c22f75a',
message: 'Sample One',
seen: false
},
{
_id: '616d779458abdc3fa4059f53',
conversationId: '61696e3ed94cd23f8c22f75a',
message: 'Sample Two',
seen: false
}
]
Frontend function that will change the value of seen
const handleUpdateSeen= (conversation) => {
dispatch(updateTaskSeenById(conversation));
};
Value that will sending to backend and the output that I want to be change on messageCollection
[
{
_id: '616d76e858abdc3fa4059ee3',
conversationId: '61696e3ed94cd23f8c22f75a',
message: 'Sample One',
seen: true
},
{
_id: '616d779458abdc3fa4059f53',
conversationId: '61696e3ed94cd23f8c22f75a',
message: 'Sample Two',
seen: true
}
]
Solution I made
export const updateMessageSeen = async (req, res) => {
try {
var updatedData;
for (let i = 0; i < req.body.length; i++) {
update = {
_id: req.body[i]._id,
conversationId: req.body[i].conversationId,
senderId: req.body[i].senderId._id,
messageText: req.body[i].messageText,
messageMedia: req.body[i].messageMedia,
seen: req.body[i].seen,
createdAt: req.body[i].createdAt,
};
}
await messageModel.updateMany(
{ conversationId: req.params.conversationId },
updatedData
);
} catch (error) {
res.status(500).json({ msg: error.message });
}
};
You may use Model.updateMany to update multiple documents in a collection.
export const seenMessageById = async (req, res) => {
try {
if (Array.isArray(req.body) && req.body.length > 0) {
const idList = req.body.map( message => message._id);
const result = await messageModel.updateMany({ _id: { $in: idList }}, { seen: true });
res.status(200).json({ msg: `Total ${result.nModified} documents updated` });
} else {
res.status(400).json({ msg: 'Atleast 1 message required to update.' });
}
} catch (error) {
res.status(500).json({ msg: error.message });
}
};

Updating a document with mongoose

I got a small problem. With this code I updating a existing document in mongodb using node.js and mongoose
router.route('/profile/:profile_id/:session_key')
.put(function(req, res) {
if (req._body == true && req.is('application/json') == 'application/json' ) {
// Get the profile to update
Profile.findOne({ profile_id: req.params.profile_id }, function(err, t_profile) {
if (err) {
res.send(err);
} else {
if(!t_profile) {
res.json({ status: 'CANT FIND PROFILE TO UPDATE' });
} else {
if (t_profile.session_key == req.params.session_key) {
// Update profile with new data
t_profile.name = setReq( t_profile.name, req.body.name );
t_profile.sex = setReq( t_profile.sex, req.body.sex );
t_profile.birthdate = setReq( t_profile.birthdate, req.body.birthdate );
t_profile.country = setReq( t_profile.country, req.body.country );
t_profile.password = setReq( t_profile.password, req.body.password );
// save updatd profile
t_profile.save( { _id: profile._id }, function(save_err, u_profile) {
if (save_err) {
res.json({ status: 'DB ERROR' });
} else {
res.json({ status: 'OK' });
}
});
} else {
res.json({ status: 'NOT LOGGED IN' });
}
//res.json({ status: "THIS RUNS"});
}
}
});
} else {
res.json({ status: 'ERROR', msg: 'Not application/json type'});
}
});
But my function inserted as a parameter in the .save() function neve runs, i need to uncomment the line res.json({ status: "THIS RUNS"}); to get som response back to the client. Why is not res.json({ status: 'DB ERROR' }); or res.json({ status: 'OK' }); sent back?
Stupid error, t_profile.save(function(save_err, u_profile) {...} fixed it.

Resources