Understanding NestJS Middleware: Creating and Applying Middleware to Handle Requests and Responses

In NestJS, middleware is a crucial component for handling requests and responses. This article will provide an in-depth understanding of the concept of middleware in NestJS and explain how to create and apply middleware to handle requests and responses.

What is Middleware

In NestJS, middleware is a function that can access the request object (req), response object (res), and the next middleware function in the application’s request-response cycle (next). Middleware can perform various operations before handling the request, such as user authentication, logging, modifying the request object, and more.

The definition and usage of middleware in NestJS are similar to Express.js, but NestJS offers more robust modularity and type safety support.

Creating Middleware

To create middleware, you need to implement the NestMiddleware interface and define the use method. Here is a simple example of a logging middleware:

1
import { Injectable, NestMiddleware } from "@nestjs/common";
2
import { Request, Response, NextFunction } from "express";
3
4
@Injectable()
5
export class LoggerMiddleware implements NestMiddleware {
6
use(req: Request, res: Response, next: NextFunction) {
7
console.log(`Request...`);
8
next();
9
}
10
}

In the example above, the LoggerMiddleware will log each request and call the next() function to pass control to the next middleware or route handler.

Applying Middleware

To apply middleware, you can use the MiddlewareConsumer in the configure method of the module. Here is an example of applying the LoggerMiddleware to all routes:

1
import { Module, NestModule, MiddlewareConsumer } from "@nestjs/common";
2
import { LoggerMiddleware } from "./logger.middleware";
3
4
@Module({
5
// other module configurations
6
})
7
export class AppModule implements NestModule {
8
configure(consumer: MiddlewareConsumer) {
9
consumer.apply(LoggerMiddleware).forRoutes("*");
10
}
11
}

The apply method specifies the middleware to be applied, and the forRoutes method specifies the scope of the middleware. forRoutes('*') means the middleware will be applied to all routes.

Middleware Examples

Modifying the Request Object

The following middleware will add a custom property customProperty to the request object:

1
import { Injectable, NestMiddleware } from "@nestjs/common";
2
import { Request, Response, NextFunction } from "express";
3
4
@Injectable()
5
export class CustomPropertyMiddleware implements NestMiddleware {
6
use(req: Request, res: Response, next: NextFunction) {
7
req["customProperty"] = "Custom Value";
8
next();
9
}
10
}

Modifying the Response Object

Here is an example of middleware that modifies the response object:

1
import { Injectable, NestMiddleware } from "@nestjs/common";
2
import { Request, Response, NextFunction } from "express";
3
4
@Injectable()
5
export class ResponseModifierMiddleware implements NestMiddleware {
6
use(req: Request, res: Response, next: NextFunction) {
7
res.on("finish", () => {
8
console.log("Response finished");
9
});
10
next();
11
}
12
}

Multiple Middleware Working Together

Multiple middleware can work together, each performing its own operations. Here is an example using both LoggerMiddleware and CustomPropertyMiddleware:

1
import { Module, NestModule, MiddlewareConsumer } from "@nestjs/common";
2
import { LoggerMiddleware } from "./logger.middleware";
3
import { CustomPropertyMiddleware } from "./custom-property.middleware";
4
5
@Module({
6
// other module configurations
7
})
8
export class AppModule implements NestModule {
9
configure(consumer: MiddlewareConsumer) {
10
consumer.apply(LoggerMiddleware, CustomPropertyMiddleware).forRoutes("*");
11
}
12
}

In this example, LoggerMiddleware logs the request, and CustomPropertyMiddleware adds a custom property to the request object. All requests will go through both middleware.

Different Usages of forRoutes and exclude

The forRoutes method can specify middleware to be applied to specific paths, controllers, or methods.

Applying to a Specific Path

1
consumer
2
.apply(LoggerMiddleware)
3
.forRoutes({ path: "cats", method: RequestMethod.GET });

Applying to a Specific Controller

1
import { CatsController } from "./cats.controller";
2
3
consumer.apply(LoggerMiddleware).forRoutes(CatsController);

Applying to Multiple Paths and Methods

1
consumer
2
.apply(LoggerMiddleware)
3
.forRoutes(
4
{ path: "cats", method: RequestMethod.GET },
5
{ path: "dogs", method: RequestMethod.POST },
6
);

Excluding Specific Paths

The exclude method is used to exclude specific paths, so middleware does not apply to these paths:

1
consumer
2
.apply(LoggerMiddleware)
3
.exclude(
4
{ path: "cats", method: RequestMethod.GET },
5
{ path: "dogs", method: RequestMethod.POST },
6
)
7
.forRoutes("*");

In this example, LoggerMiddleware will be applied to all paths except GET /cats and POST /dogs.

Functional Middleware

Besides class-based middleware, NestJS also supports creating middleware using functions. Here is an example of functional middleware:

1
import { Request, Response, NextFunction } from "express";
2
3
export function logger(req: Request, res: Response, next: NextFunction) {
4
console.log(`Request...`);
5
next();
6
}

When using functional middleware, you do not need to implement the NestMiddleware interface. You can directly apply it in the module:

1
import { Module, NestModule, MiddlewareConsumer } from "@nestjs/common";
2
import { logger } from "./logger.middleware";
3
4
@Module({
5
// other module configurations
6
})
7
export class AppModule implements NestModule {
8
configure(consumer: MiddlewareConsumer) {
9
consumer.apply(logger).forRoutes("*");
10
}
11
}

Conclusion

This article provided an in-depth understanding of the concept of middleware in NestJS and explained how to create and apply middleware to handle requests and responses. By using middleware, developers can perform various operations during request processing, such as authentication, logging, and request modification, enhancing the functionality and security of applications. We also demonstrated examples of multiple middleware working together, modifying request and response objects, and using the forRoutes and exclude methods to control the scope of middleware. Additionally, we introduced how to create and apply functional middleware.