Updating MongoDB documents in Node.js uses collection.updateOne(), collection.updateMany(), and collection.findOneAndUpdate() paired with atomic update operators like $set, $inc, and $push. Using update operators modifies only specific fields safely under concurrent requests without overwriting unrelated fields.
$set, $inc, and updatedAtAlways wrap changes inside an operator object ({ $set: { ... } }). Never pass a raw field object { name: 'New' } directly as the second argument—modern MongoDB drivers throw an error to stop accidental full-document replacement.
import { ObjectId } from 'mongodb';
async function updateProductStock(productsCollection, productId, soldQty, newTitle) {
if (!ObjectId.isValid(productId)) {
throw new Error('Invalid product ID');
}
const result = await productsCollection.updateOne(
{ _id: new ObjectId(productId), stock: { $gte: soldQty } },
{
$inc: { stock: -soldQty },
$set: { title: newTitle, updatedAt: new Date() },
}
);
return {
matched: result.matchedCount,
modified: result.modifiedCount,
};
}
Notice how placing stock: { $gte: soldQty } inside the filter and $inc: { stock: -soldQty } inside the update makes inventory deduction atomic—preventing race conditions where two simultaneous buyers drive stock negative!
findOneAndUpdate()When an API route needs the updated record right away, call collection.findOneAndUpdate(filter, update, { returnDocument: 'after' }). Remember to use returnDocument: 'after' in driver v4+ (replacing the older returnOriginal: false option).
modifiedCount === 0 to assume a document was not found: If the submitted field already holds the exact same value, matchedCount is 1 while modifiedCount is 0. Check matchedCount for existence.$push without $addToSet when duplicates are unwanted: $push appends duplicate tags every time; use $addToSet when an array should behave like a unique set.req.body straight into { $set: req.body }: Pick only allowed editable fields explicitly so callers cannot inject $set: { isAdmin: true }.{ upsert: true } do in updateOne()?If a document matches the filter, MongoDB updates it. If zero documents match, MongoDB inserts a new document combining the filter fields and update operators.
updateOne() and replaceOne()?updateOne() applies atomic $set/$inc changes to targeted fields, whereas replaceOne() replaces the entire document body (keeping _id intact).
Use the $unset operator: updateOne({ _id }, { $unset: { legacyNotes: '' } }).
Sign in to track your learning journey, earn industry-recognized certificates, and join our elite developer community.
Quick Access With
Experiment with the code from this lesson in our interactive playground.
You've completed this section! Take a quick 5-question quiz to check your understanding.