What is middleware in NestJS and how does it differ from guards?
Middleware in NestJS
Middleware in NestJS is identical to Express middleware β a function that runs before the route handler and has access to req, res, and next. However, NestJS wraps it in a class-based format that supports dependency injection.
Creating Middleware
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class LoggerMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
next();
}
}Functional Middleware (Simple)
// No class needed β just a function
export function loggerMiddleware(req: Request, res: Response, next: NextFunction) {
console.log(`${req.method} ${req.url}`);
next();
}Registering Middleware
Unlike Guards, Pipes, and Interceptors, NestJS middleware is registered in the module using the configure() method:
import { Module, NestModule, MiddlewareConsumer, RequestMethod } from '@nestjs/common';
@Module({
controllers: [UsersController, ProductsController],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
consumer
.apply(LoggerMiddleware)
.forRoutes('*'); // all routes
consumer
.apply(AuthMiddleware)
.exclude(
{ path: 'auth/login', method: RequestMethod.POST },
{ path: 'auth/register', method: RequestMethod.POST },
)
.forRoutes(UsersController); // all UsersController routes
consumer
.apply(RateLimitMiddleware)
.forRoutes({ path: 'api/*', method: RequestMethod.ALL });
// Multiple middleware in order
consumer
.apply(CorsMiddleware, HelmetMiddleware, LoggerMiddleware)
.forRoutes('*');
}
}Practical Example: Request ID Middleware
import { v4 as uuid } from 'uuid';
@Injectable()
export class RequestIdMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
const requestId = req.headers['x-request-id'] as string || uuid();
req['requestId'] = requestId;
res.setHeader('x-request-id', requestId);
next();
}
}Middleware vs Guards vs Interceptors
| Feature | Middleware | Guard | Interceptor |
|---|---|---|---|
| Access to ExecutionContext | β | β | β |
| Access to metadata (Reflector) | β | β | β |
| Runs before guards | β | β | β |
| DI support | β | β | β |
| Can abort request | β | β | β |
| Can modify response | β | β | β |
| Purpose | Logging, CORS, body parse | Auth, roles | Transform, cache, timing |
Execution Order
Request
β Middleware (in order registered)
β Guards
β Interceptors (before)
β Pipes
β Handler
β Interceptors (after)
β ResponseMiddleware runs before guards and has no access to NestJS metadata β use guards for auth/authorization, middleware for lower-level concerns (logging, CORS, request parsing).
Summary
NestJS Middleware works like Express middleware but with class syntax and DI support. Register it via configure() in the module. Use middleware for infrastructure concerns (logging, request IDs, rate limiting with Express packages). Use Guards for authentication/authorization β they have access to route metadata and the full ExecutionContext.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.