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
password
field 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 theauthor
field 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)