How to Use Middleware in Mongoose: Syntax and Examples
In Mongoose, you use
middleware by defining pre or post hooks on a schema with schema.pre() or schema.post(). These hooks run functions before or after events like save, remove, or find to add custom logic.Syntax
Mongoose middleware is defined on a schema using schema.pre() for actions before an event, and schema.post() for actions after an event. You specify the event name like save or remove, and provide a callback function that runs at that time.
The callback can be asynchronous and receives a next function to continue the operation.
javascript
schema.pre('save', function(next) { // code to run before saving next(); }); schema.post('remove', function(doc) { // code to run after removing });
Example
This example shows how to use a pre-save middleware to automatically set a timestamp before saving a document, and a post-remove middleware to log when a document is deleted.
javascript
const mongoose = require('mongoose'); const userSchema = new mongoose.Schema({ name: String, createdAt: Date }); // Pre-save middleware to set createdAt userSchema.pre('save', function(next) { if (!this.createdAt) { this.createdAt = new Date(); } next(); }); // Post-remove middleware to log deletion userSchema.post('remove', function(doc) { console.log(`User ${doc.name} was removed.`); }); const User = mongoose.model('User', userSchema); async function run() { await mongoose.connect('mongodb://localhost:27017/testdb'); const user = new User({ name: 'Alice' }); await user.save(); console.log('User saved:', user); await user.remove(); await mongoose.disconnect(); } run();
Output
User saved: { _id: ..., name: 'Alice', createdAt: 2024-06-01T... }
User Alice was removed.
Common Pitfalls
- Forgetting to call
next()in pre middleware causes the operation to hang. - Using arrow functions (
() => {}) for middleware breaksthisbinding; always use regular functions. - Not handling asynchronous code properly can cause unexpected behavior; use
asyncfunctions or callnext()after async operations.
javascript
/* Wrong: arrow function loses 'this' context */ schema.pre('save', (next) => { console.log(this); // undefined next(); }); /* Right: regular function keeps 'this' */ schema.pre('save', function(next) { console.log(this); // document being saved next(); });
Quick Reference
| Middleware Type | When It Runs | Example Event | Notes |
|---|---|---|---|
| Pre | Before an event | save, remove, validate | Call next() to continue |
| Post | After an event | save, remove, find | Receives document or result |
| Query | Before/after queries | find, update | Use for query middleware |
| Aggregate | Before/after aggregation | aggregate | Use for aggregation middleware |
Key Takeaways
Use schema.pre() for middleware before events and schema.post() for after events.
Always use regular functions, not arrow functions, to access the document with this.
Call next() in pre middleware to avoid hanging operations.
Middleware can be async; use async/await or call next() after async work.
Middleware works on schema level to add reusable logic for database operations.