0
0
NestjsComparisonBeginner · 4 min read

Middleware vs Guard vs Interceptor in NestJS: Key Differences and Usage

In NestJS, 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.

AspectMiddlewareGuardInterceptor
PurposeModify request/response before route handlerAuthorize or block requestsTransform response or add extra logic after handler
Execution TimeBefore route handlerBefore route handler, after middlewareBefore and after route handler
Access to Handler ResultNoNoYes
Typical Use CasesLogging, parsing, CORSAuthentication, roles checkResponse formatting, caching, logging
Can Stop RequestYes (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

typescript
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();
  }
}
Output
Logs the request URL to the console before passing control to the next handler.
↔️

Guard Equivalent

typescript
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']);
  }
}
Output
Allows the request only if an 'authorization' header is present; otherwise blocks access.
🎯

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.

Key Takeaways

Middleware modifies requests or responses before route handlers and can end requests early.
Guards control access by allowing or denying requests based on authorization logic before handlers.
Interceptors run before and after handlers to transform responses or add extra processing.
Use middleware for general request processing, guards for security checks, and interceptors for response handling.
Each plays a unique role in NestJS's request lifecycle for clean and modular code.