Middleware vs Guard vs Interceptor in NestJS: Key Differences and Usage
Middleware runs before route handlers to modify requests or responses, Guards decide if a request can proceed based on authorization logic, and Interceptors handle extra processing like logging or transforming responses after the handler. Each serves a distinct role in the request lifecycle for clean, modular code.Quick Comparison
This table summarizes the main differences between Middleware, Guard, and Interceptor in NestJS.
| Aspect | Middleware | Guard | Interceptor |
|---|---|---|---|
| Purpose | Modify request/response before route handler | Authorize or block requests | Transform response or add extra logic after handler |
| Execution Time | Before route handler | Before route handler, after middleware | Before and after route handler |
| Access to Handler Result | No | No | Yes |
| Typical Use Cases | Logging, parsing, CORS | Authentication, roles check | Response formatting, caching, logging |
| Can Stop Request | Yes (by ending response) | Yes (by denying access) | No (only modifies output) |
Key Differences
Middleware in NestJS acts like a gatekeeper that runs before the route handler and can modify the incoming request or outgoing response directly. It is similar to Express middleware and is useful for tasks like logging, parsing JSON, or setting headers.
Guards are specialized components that decide if a request should continue to the route handler based on authorization logic. They run after middleware but before the handler and cannot modify the response, only allow or deny access.
Interceptors wrap around the route handler execution. They can run code before and after the handler, allowing them to transform the response, add logging, or handle caching. Unlike guards, interceptors have access to the handler's result and can modify it before sending it to the client.
Code Comparison
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(`Request to: ${req.originalUrl}`); next(); } }
Guard Equivalent
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { Observable } from 'rxjs'; @Injectable() export class AuthGuard implements CanActivate { canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> { const request = context.switchToHttp().getRequest(); return Boolean(request.headers['authorization']); } }
When to Use Which
Choose Middleware when you need to modify requests or responses globally or for many routes, such as adding headers, logging, or parsing bodies.
Choose Guard when you want to protect routes by checking if a user is authorized or authenticated before allowing access.
Choose Interceptor when you want to transform the response, add logging around the handler, or implement caching after the route handler runs.