Middleware
Middleware is a function that receives the request before the controller does. It is the same middleware as in Express: it has req, res and a next() function that passes the request on. It is used for shared tasks — logging requests, setting headers, measuring time.
Creating it
nest g middleware common/middleware/request-log --flat// src/common/middleware/request-log.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { NextFunction, Request, Response } from 'express';
@Injectable()
export class RequestLogMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log(`${req.method} ${req.originalUrl}`);
next();
}
}The class implements the NestMiddleware interface with a use() method. Calling next() is required: without it the request stays stuck in the middleware.
Applying it
Middleware has no place in the @Module() decorator. It is applied in the module's configure() method:
// src/app.module.ts
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
import { RequestLogMiddleware } from './common/middleware/request-log.middleware';
import { PostsModule } from './posts/posts.module';
@Module({
imports: [PostsModule],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer.apply(RequestLogMiddleware).forRoutes('posts');
}
}apply() names the middleware, forRoutes() the addresses it runs for. Open /posts and /posts/1 — the server terminal shows GET /posts and GET /posts/1.
Choosing routes
// src/app.module.ts
import { MiddlewareConsumer, RequestMethod } from '@nestjs/common';
import { PostsController } from './posts/posts.controller';
configure(consumer: MiddlewareConsumer) {
consumer
.apply(RequestLogMiddleware)
.exclude({ path: 'posts/popular', method: RequestMethod.GET })
.forRoutes(PostsController);
}You can pass a controller to forRoutes() — the middleware then runs for all its routes. exclude() removes individual addresses. Several middleware are listed with commas: apply(First, Second).
A function instead of a class
If the middleware needs no services, a plain function is enough:
// src/common/middleware/powered-by.middleware.ts
import { NextFunction, Request, Response } from 'express';
export function poweredBy(req: Request, res: Response, next: NextFunction) {
res.setHeader('X-Powered-By', 'Blog API');
next();
}It is applied the same way: consumer.apply(poweredBy).forRoutes('posts'). Ready-made Express middleware for the whole application can be enabled in src/main.ts with app.use().
Middleware or other tools
Middleware does not know which controller and method will be called. That is why access checks are better done with guards, and response changes with interceptors: both see exactly which method handles the request. These tools are covered in the next lessons.
Common mistake
A forgotten next() call makes the request hang: the client waits for a response until it times out, and the log shows no error. If the server stopped responding after you added middleware, first check that next() is called in every branch of the code.