Bordered avatar

Street Learner

Author
6 min read

Last Updated: a year ago

Middleware vs Guards vs Interceptors in NestJS: Lifecycle, Auth, and E-Commerce Examples

Middleware vs Guards vs Interceptors in NestJS: Lifecycle, Auth, and E-Commerce Examples

When building NestJS applications, senior developers know that where you put your logic is just as important as what logic you write. NestJS offers multiple layers to handle requests:

  • Middleware
  • Guards
  • Interceptors

Each layer has a unique execution point, making it ideal for specific tasks like authentication, authorization, caching, or logging. Misplacing your logic can lead to security holes, performance issues, or messy code.

NestJS Request Lifecycle

When a request arrives, NestJS executes layers in this order:

  1. Middleware – runs first, before route selection
  2. Guard – checks if the user can access a route
  3. Pipe – transforms or validates input
  4. Interceptor (before handler) – pre-processing logic
  5. Controller / Route Handler – actual business logic
  6. Interceptor (after handler) – post-processing, like caching
  7. Exception Filter – catches errors

Understanding this order is crucial to placing authentication, authorization, and caching logic correctly.

What Each Layer Does

Middleware

Middleware runs before NestJS selects a route. It has access to raw requests and responses.

Use middleware for:

  • Authenticating users with JWT tokens
  • Attaching user information to req object
  • Logging requests for analytics
  • Blocking unauthorized access immediately

Key Advantage: Stops requests early without hitting controllers or services.

Guards

Guards run after route selection but before controller execution. They decide if a user can perform an action.

Use guards for:

  • Role-based access control (RBAC)
  • Permission checks (ABAC)
  • Protecting admin-only routes
  • Conditional access based on business rules

Key Advantage: Knows the context of the route and can block execution precisely.

Interceptors

Interceptors wrap around the route execution. They can run before and after the controller.

Use interceptors for:

  • Caching responses
  • Logging execution time
  • Transforming or shaping the response
  • Handling global concerns without touching business logic

Important: Interceptors are not ideal for authentication because they run after guards and route resolution. This makes them slower and less secure for blocking unauthorized requests.

Why Middleware is Better for Authentication than Interceptors

  • Executes first → stops bad requests before they reach controllers
  • Prevents unnecessary execution of guards, pipes, and interceptors for unauthorized users
  • Provides a centralized place for all auth logic
  • Keeps performance high under heavy traffic

Example Misuse: If you authenticate in an interceptor, the request may already pass through guards or hit caching logic, causing performance or security problems.

E-Commerce Example: Auth with Middleware + Caching with Interceptors

Imagine a shopping backend:

  • Middleware validates JWT tokens and attaches user info to requests
  • Guards check if the user is allowed to update products
  • Interceptors cache product listings for faster response

Middleware Example

@Injectable()
export class AuthMiddleware implements NestMiddleware {
  use(req: Request, res: Response, next: NextFunction) {
    const token = req.headers['authorization'];
    if (!token || token !== 'Bearer valid-shop-token') {
      throw new UnauthorizedException('You are not allowed');
    }
    req['user'] = { id: '9001', name: 'Hamza', role: 'customer' };
    next();
  }
}

This middleware blocks unauthorized requests immediately and attaches user info for downstream logic.

Guard Example

@Injectable()
export class RolesGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const req = context.switchToHttp().getRequest();
    return req.user?.role === 'admin'; // Only admin can update products
  }
}

Interceptor Example

@Injectable()
export class CacheInterceptor implements NestInterceptor {
  intercept(context: ExecutionContext, next: CallHandler) {
    console.log('Caching product listing...');
    return next.handle().pipe(
      tap(response => {
        // Here, you could save the response in Redis or memory cache
      }),
    );
  }
}

Real-World E-Commerce Scenarios

  • Middleware → Verify user token before accessing cart or checkout
  • Guard → Only admin can add or update products
  • Interceptor → Cache product listing, homepage banners, or popular categories

This combination ensures security, performance, and maintainability.

Senior Developer Takeaways

  1. Middleware = Identity/Auth → stop requests early
  2. Guard = Authorization/Permissions → control access per route
  3. Interceptor = Caching/Logging/Transforming → wrap business logic, don’t block requests
  4. Never use interceptors for authentication
  5. Middleware is fast, central, and efficient for global auth

Conclusion

Understanding the NestJS request lifecycle and choosing the right layer for your logic is a key skill for senior developers. In an e-commerce app:

  • Middleware secures your endpoints
  • Guards enforce business rules
  • Interceptors optimize response speed

Together, these layers make your application secure, scalable, and high-performing.

Related Stories



Performance Optimization of NestJS Applications: A Complete Guide

Performance Optimization of NestJS Applications: A Complete Guide

Optimizing the performance of a NestJS application is critical for building scalable, fast, and production-ready APIs. Even though NestJS is a high-performance framework, improper coding practices, unoptimized database queries, and lack of caching can slow down your application.

Bordered avatar

Street Learner

21 Jan 2025

Mastering High-Performance Interceptors in NestJS for Scalable APIs

Mastering High-Performance Interceptors in NestJS for Scalable APIs

NestJS interceptors are one of the most powerful tools in the framework, enabling developers to transform responses, cache results, log performance, and optimize requests. For large-scale applications, building high-performance interceptors is essential to improve speed, maintainability, and scalability.

Bordered avatar

Street Learner

18 Jan 2025