Mongoose provides powerful pre and post middleware hooks that allow you to execute logic before or after an operation (e.g., save, find, remove). These hooks are useful for data validation, transformation, logging, security, and more π.
During my recent endeavor to migrate the entire existing codebase to NEST, I discovered a set of tools that proved invaluable.
In this blog, we'll explore pre and post middleware in Mongoose using NestJS and TypeScript.
What is Middleware in Mongoose? π€
Middleware (also called hooks) in Mongoose allows you to run functions before (pre) or after (post) certain operations on documents.
Supported Operations βοΈ
Middleware can be applied to:
-
Document operations:
save,remove,validate -
Query operations:
find,findOne,findOneAndUpdate,deleteOne -
Aggregate operations:
aggregate
Setting Up NestJS with Mongoose β‘
First, install Mongoose and its TypeScript types in a NestJS project:
npm install @nestjs/mongoose mongoose
npm install --save-dev @types/mongoose
Next, let's create a User model with pre and post middleware.
Using pre Middleware in NestJS π
Example 1: Hashing Password Before Saving
A common use case for pre middleware is hashing a password before saving it to the database.
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document } from 'mongoose';
import * as bcrypt from 'bcrypt';
@Schema()
export class User extends Document {
@Prop({ required: true })
username: string;
@Prop({ required: true })
password: string;
}
const UserSchema = SchemaFactory.createForClass(User);
// Pre-save hook to hash password before saving
UserSchema.pre<User>('save', async function (next) {
if (!this.isModified('password')) return next();
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
next();
});
export { UserSchema };
How it Works? π
- The
pre('save')middleware runs before saving a user. - It checks if the
passwordfield has been modified. - If modified, it hashes the password using
bcrypt. - Finally, the
next()function is called to continue the save process.
Using post Middleware in NestJS π£
Example 2: Logging User Creation
We can use post middleware to log when a new user is created.
UserSchema.post<User>('save', function (doc) {
console.log(`New user created: ${doc.username}`);
});
How it Works? π
- The
post('save')middleware executes after a user is successfully saved. - It logs the created user's username.
Using pre Middleware for Query Operations π
Example 3: Auto-Populating a Reference Before Querying
Let's say a Post model has an author field referencing the User model. We can automatically populate the author field before fetching a post.
import { Schema, SchemaFactory } from '@nestjs/mongoose';
import { Document, Types } from 'mongoose';
import { User } from './user.schema';
@Schema()
export class Post extends Document {
@Prop({ required: true })
title: string;
@Prop({ type: Types.ObjectId, ref: 'User', required: true })
author: User;
}
const PostSchema = SchemaFactory.createForClass(Post);
// Pre-find hook to auto-populate author
PostSchema.pre('find', function () {
this.populate('author');
});
export { PostSchema };
How it Works? π
-
pre('find')runs before executing afind()query. -
this.populate('author')ensures that theauthorfield is automatically populated.
Using post Middleware for Query Operations π
Example 4: Logging After a User is Found
We can use post middleware to log user retrievals.
UserSchema.post('findOne', function (doc) {
if (doc) {
console.log(`User found: ${doc.username}`);
}
});
How it Works? ποΈ
-
post('findOne')runs after a document is found. - If a user is found, it logs their username.
Pre and Post Middleware for Deleting Documents ποΈ
Example 5: Cleaning Up Related Data Before Deleting a User
If a user is deleted, we might want to remove their associated posts.
UserSchema.pre('deleteOne', { document: true, query: false }, async function (next) {
const userId = this._id;
await PostModel.deleteMany({ author: userId });
console.log(`Deleted posts by user: ${userId}`);
next();
});
How it Works? π§Ή
-
pre('deleteOne')runs before deleting a user. - It removes all posts associated with the user.
Conclusion π―
Mongoose middleware (pre and post) is a powerful tool for extending document behavior in a NestJS application. Some common use cases include:
β
Hashing passwords before saving users
β
Auto-populating referenced fields
β
Logging events after CRUD operations
β
Cleaning up related data before deleting documents
By leveraging these hooks, you can enhance data integrity, security, and performance in your NestJS applications.
Would you like to see middleware used in a real-world NestJS app? Let me know in the comments! π
Top comments (0)