Facebook Pixel

Mongoose Middleware: pre and post Hooks Explained

Mongoose middleware (pre and post hooks) is powerful. Here is how to use it.

Mongoose Middleware: pre and post Hooks Explained

Mongoose middleware lets you run functions before or after certain operations. Called pre and post hooks. Useful for hashing passwords, logging, sending emails, and more.

When Hooks Run

Hooks run on init, validate, save, remove, updateOne, findOneAndDelete, and similar. The most common are pre('save') and post('save').

pre('save') Example: Hash Password

userSchema.pre('save', async function(next) {
  if (!this.isModified('passwordHash')) return next();
  this.passwordHash = await bcrypt.hash(this.passwordHash, 10);
  next();
});

Hash the password only when it has changed. Skip otherwise (e.g., profile updates).

post('save') Example: Send Welcome Email

userSchema.post('save', function(doc) {
  sendWelcomeEmail(doc.email);
});

Runs after the save succeeds. Do not block the request on this; use a queue for real apps.

pre('validate') vs pre('save')

pre('validate') runs before validation. pre('save') runs after validation, before writing. Use pre('validate') to set defaults or normalize fields; use pre('save') for things like hashing that depend on validation passing.

Hooks on Updates

userSchema.pre('findOneAndUpdate', async function(next) {
  const update = this.getUpdate();
  if (update.passwordHash) {
    update.passwordHash = await bcrypt.hash(update.passwordHash, 10);
  }
  next();
});

Hooks do not run on updateOne or findByIdAndUpdate by default; you need to register them on the appropriate method.

Errors in Hooks

If a pre hook calls next(err), the operation aborts. If it throws, the operation aborts. Use this for validations that depend on async work.

Beware: this in Arrow Functions

Hooks use this to refer to the document. Do not use arrow functions for hooks; they do not bind this.

Do Not Put Business Logic in Hooks

Hooks are for data-related concerns (hashing, normalizing, logging). Business logic (sending emails, charging payments) belongs in services or controllers. Otherwise, hooks become a tangled web that is hard to test.

The Takeaway

Mongoose middleware (pre and post hooks) runs before or after operations like save, validate, update. Use pre('save') for hashing and normalizing, post('save') for side effects (with a queue). Do not use arrow functions (this would be undefined). Keep business logic out of hooks.

Middleware functions that run before or after operations like save, validate, update. pre('save') runs before writing; post('save') runs after. Useful for hashing, normalizing, logging, and side effects.

Use a pre('save') hook. Check if the password field is modified with this.isModified('passwordHash'). If yes, hash with bcrypt and set this.passwordHash. Call next() to continue. Skip hashing otherwise (e.g., profile updates).

Hooks use this to refer to the document being saved. Arrow functions do not bind this, so this would be undefined. Use regular functions (function(next) { ... }) instead.

Not by default. pre('save') runs on save; for updates you need to register hooks on findOneAndUpdate or updateOne explicitly. Be careful when updating passwords through these methods; hash them in the hook.

Hooks are for data-related concerns (hashing, normalizing, logging). Business logic (emails, payments) in hooks becomes a tangled web that is hard to test and reason about. Put it in services or controllers instead.

Ready to master Node.js completely?

Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.

Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.