乐闻世界logo
搜索文章和话题

How do Mongoose middleware and hooks work, and what are their use cases?

2月22日 20:12

Mongoose Middleware and Hooks are powerful features that allow executing custom logic before or after certain operations. Middleware is divided into two categories: Document Middleware and Query Middleware.

Middleware Types

1. Document Middleware

Operations performed on document instances, such as save(), validate(), remove(), etc.

javascript
userSchema.pre('save', function(next) { console.log('About to save user:', this.name); next(); }); userSchema.post('save', function(doc) { console.log('User saved:', doc.name); });

2. Query Middleware

Operations performed on Model queries, such as find(), findOne(), updateOne(), etc.

javascript
userSchema.pre('find', function() { this.where({ deleted: false }); }); userSchema.post('find', function(docs) { console.log('Found', docs.length, 'users'); });

Common Hooks

Document Operation Hooks

  • validate - Validate document
  • save - Save document
  • remove - Remove document
  • init - Initialize document (load from database)

Query Operation Hooks

  • count - Count query
  • find - Find documents
  • findOne - Find single document
  • findOneAndDelete - Find and delete
  • findOneAndUpdate - Find and update
  • updateOne - Update single document
  • updateMany - Update multiple documents
  • deleteOne - Delete single document
  • deleteMany - Delete multiple documents

Difference Between Pre and Post Hooks

Pre Hooks

  • Run before operation execution
  • Can modify data or abort operation
  • Must call next() or return Promise
  • Can access this (document instance or query object)
javascript
userSchema.pre('save', function(next) { if (this.age < 0) { const err = new Error('Age cannot be negative'); return next(err); } this.email = this.email.toLowerCase(); next(); });

Post Hooks

  • Run after operation execution
  • Cannot modify data or abort operation
  • Receive operation result as parameter
  • Can access this (document instance or query object)
javascript
userSchema.post('save', function(doc) { console.log('User saved with ID:', doc._id); // Send notifications, log, etc. });

Async Middleware

Mongoose middleware supports async operations:

javascript
// Using async/await userSchema.pre('save', async function(next) { const existing = await this.constructor.findOne({ email: this.email }); if (existing && existing._id.toString() !== this._id.toString()) { const err = new Error('Email already exists'); return next(err); } next(); }); // Return Promise userSchema.pre('save', function() { return checkEmailAvailability(this.email).then(isAvailable => { if (!isAvailable) { throw new Error('Email already exists'); } }); });

Practical Use Cases

  1. Password Hashing: Encrypt password before saving user
  2. Timestamps: Automatically set createdAt and updatedAt
  3. Soft Delete: Mark as deleted before actual deletion
  4. Data Validation: Execute complex validation logic
  5. Logging: Record operation history
  6. Cache Invalidation: Update related caches
  7. Related Data: Automatically update related documents
  8. Notifications: Send notifications after operations

Important Notes

  1. Middleware executes in definition order
  2. Errors in pre hooks abort the operation
  3. Query middleware doesn't trigger document middleware
  4. Use { runValidators: true } with findOneAndUpdate to trigger validation
  5. Avoid infinite loops in middleware
标签:Mongoose