gotchajavascriptmongooseMajor
Mongoose middleware (hooks): pre/post pitfalls
Viewed 0 times
mongoosemiddlewarehooksprepostfindByIdAndUpdatesavedocument middlewarequery middleware
Problem
Mongoose pre/post hooks do not fire for all operation types. Hooks registered on 'save' do not trigger on findByIdAndUpdate, updateMany, or deleteMany, silently skipping validation, hashing, or audit logging.
Solution
Register hooks on the query operations you actually use, or replace query-based updates with document save() calls. For consistent behavior, use document middleware (save, validate) and fetch-then-modify patterns instead of query middleware shortcuts.
Why
Mongoose has separate document middleware (operates on document instances) and query middleware (operates on query objects). findByIdAndUpdate bypasses document middleware entirely and goes directly to the driver.
Gotchas
- this in query middleware refers to the query, not the document
- Pre 'save' runs before validators; pre 'validate' runs before that
- post middleware receives the result, not the query — use post('save', function(doc)) not function(result)
- Calling next() in pre middleware is optional in async functions but required in callback-style hooks
Code Snippets
Hook that fires for both save and query updates
// Fires on .save() only — NOT on findByIdAndUpdate
userSchema.pre('save', async function() {
if (this.isModified('password')) {
this.password = await bcrypt.hash(this.password, 12);
}
});
// To also cover query updates:
userSchema.pre(['findOneAndUpdate', 'updateOne', 'updateMany'], async function() {
const update = this.getUpdate();
if (update.password) {
update.password = await bcrypt.hash(update.password, 12);
}
});Revisions (0)
No revisions yet.