Suggest an editImprove this articleRefine the answer for “What is middleware in NestJS and how does it differ from guards?”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**Middleware in NestJS** runs at the Express layer before guards, pipes, and interceptors. It has access to `req`, `res`, and `next()` but cannot read route metadata or `ExecutionContext`. ```typescript @Injectable() export class LoggerMiddleware implements NestMiddleware { use(req: Request, res: Response, next: NextFunction) { console.log(`${req.method} ${req.url}`); // runs before any guard next(); } } ``` **Key point:** Use middleware for logging, CORS, and request parsing. Use guards for authentication and role-based access checks.Shown above the full answer for quick recall.Answer (EN)Image**Middleware in NestJS** is a function that intercepts every HTTP request before it reaches a route handler, with direct access to Express's `req`, `res`, and `next()`. ## Theory ### TL;DR - Middleware runs at the Express layer, before any NestJS processing (pipes, guards, interceptors) - Guards run after middleware, per-route, with access to `ExecutionContext` and route metadata - Think of middleware as airport security (every passenger passes through) and guards as gate agents (check tickets per flight) - Decision rule: logging, CORS, body parsing -> middleware. JWT validation, role checks -> guards - Never use guards for CORS. Preflight requests never reach them ### Quick example ```typescript // logger.middleware.ts 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(`${req.method} ${req.url} at ${new Date().toISOString()}`); // Output: "GET /users at 2024-01-15T12:00:00Z" next(); // without this, the request hangs forever } } // app.module.ts - runs BEFORE any guard export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { consumer.apply(LoggerMiddleware).forRoutes('*'); } } ``` Every request hits this log before NestJS checks authentication. The `next()` call is what advances the request through the pipeline. ### Key difference Middleware executes at the raw Express layer. It gets `req`, `res`, and `next()` - nothing more. A guard executes later, with a NestJS `ExecutionContext` that lets it read decorators, reflect metadata, and make route-aware decisions. Middleware doesn't know which controller a request is heading to. A guard does, and that's why auth belongs in guards. ### When to use - App-wide logging, CORS headers, compression -> middleware. It applies uniformly to all matching paths before NestJS does any processing. - JWT validation, role-based access, ownership checks -> guards. They can read `@Roles()` decorator metadata and throw a proper `ForbiddenException`. - Request ID injection, tenant ID extraction from subdomain -> middleware. This runs early enough to attach data that later handlers can use. - CORS specifically -> always middleware or `app.enableCors()`. Guards run too late; preflight requests fail. - Business logic -> neither. That belongs in services. ### Comparison table | Aspect | Middleware | Guards | |---|---|---| | Execution timing | Earliest - Express layer, before pipes | After middleware, before handler | | Scope | All paths or specific paths/modules | Per-route, per-controller, or global | | Access | `req`, `res`, `next()` | `ExecutionContext` (NestJS-aware) | | Short-circuit | `res.send()` or skip `next()` | Return `false` or throw exception | | DI support | Class-based only | Class-based or functional | | Can read route metadata | No | Yes, via `Reflector` | | Best for | Logging, CORS, rate limiting | Auth, roles, ownership | ### Execution order ``` HTTP Request → Middleware (in registration order) → Guards → Interceptors (before handler) → Pipes → Route Handler → Interceptors (after handler) → Response ``` NestJS compiles middleware into Express's middleware stack via `app.use()` under the hood. The `configure(consumer)` method builds path-matched routers that execute sequentially per request. Guards bind later via metadata reflection on controllers and routes, which is also why they can read custom decorators. ### Registering middleware Guards attach with a decorator. Middleware goes through the module's `configure()` method: ```typescript export class AppModule implements NestModule { configure(consumer: MiddlewareConsumer) { // Apply to all routes consumer.apply(LoggerMiddleware).forRoutes('*'); // Apply to a specific controller, excluding public auth routes consumer .apply(AuthMiddleware) .exclude( { path: 'auth/login', method: RequestMethod.POST }, { path: 'auth/register', method: RequestMethod.POST }, ) .forRoutes(UsersController); // Chain multiple middleware - they run in this exact order consumer .apply(CorsMiddleware, HelmetMiddleware, LoggerMiddleware) .forRoutes('*'); } } ``` A guard, by contrast, attaches with a single decorator anywhere: `@UseGuards(JwtAuthGuard)`. ### Common mistakes **Forgetting `next()`:** ```typescript // Wrong - request hangs indefinitely use(req: Request) { console.log('logged'); // no next() call } // Right use(req: Request, res: Response, next: NextFunction) { console.log('logged'); next(); } ``` This is behind most "my NestJS server stopped responding" reports on Stack Overflow. **Passing an instance instead of the class:** ```typescript // Wrong - breaks DI, NestJS cannot inject dependencies consumer.apply(new LoggerMiddleware()).forRoutes('*'); // Right - NestJS handles instantiation and injection consumer.apply(LoggerMiddleware).forRoutes('*'); ``` **Using guards for CORS:** ```typescript // Wrong - runs too late, OPTIONS preflight request fails @UseGuards(CorsGuard) @Get() findAll() {} // Right consumer.apply(cors()).forRoutes('*'); // or in main.ts: app.enableCors() ``` **Async middleware without error passing:** ```typescript // Risky - thrown errors are swallowed in NestJS v9+ async use(req: Request, res: Response, next: NextFunction) { await someDbCheck(); // if this throws, nothing happens next(); } // Safe async use(req: Request, res: Response, next: NextFunction) { try { await someDbCheck(); next(); } catch (err) { next(err); // pass to Express error handler } } ``` ### Real-world usage - API gateways: `express-rate-limit` as middleware before any route processing - Multi-tenant apps: extract tenant ID from subdomain in middleware, attach to `req` for controllers and guards to consume - Audit trails: log masked user IDs (`req.headers['x-user-id'].slice(0, 4) + '****'`) before business logic touches the request - Security headers: `helmet()` as middleware before any handler - Same app, guards handle: JWT validation with `@UseGuards(JwtAuthGuard)` on protected controllers One pattern that works well in production: lightweight logging middleware on all routes, `RateLimitMiddleware` on `api/*`, then guards for actual auth. Each layer does exactly one job. ### Follow-up questions **Q:** When in the request lifecycle does middleware run relative to interceptors? **A:** Middleware runs first, at the Express layer. After that: guards, interceptors (before handler), pipes, the handler, then interceptors (after handler). **Q:** How do you apply middleware to specific HTTP methods only? **A:** Use `forRoutes({ path: '/users', method: RequestMethod.POST })`. You can chain multiple `forRoutes` calls on one `apply` for different method/path combinations. **Q:** What is the performance difference between class-based and functional middleware? **A:** Functional middleware has no DI container overhead and a slightly lower memory footprint. Use it for stateless tasks like simple logging. Use class-based when you need to inject services. **Q:** Can middleware read custom decorators or route metadata? **A:** No. Middleware has no `ExecutionContext` and no access to `Reflector`. If you need to read `@Roles()` or any custom decorator metadata, that logic belongs in a guard. **Q:** In a microservice using gRPC transport, does middleware apply? **A:** No. Middleware is HTTP-only and tied to the HTTP adapter (Express or Fastify). For gRPC, use interceptors and handle transport-level metadata via `RpcException`. ## Examples ### Basic: logging middleware with timing ```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) { const start = Date.now(); res.on('finish', () => { // Logs after response is sent: "GET /api/users 200 45ms" console.log(`${req.method} ${req.url} ${res.statusCode} ${Date.now() - start}ms`); }); next(); } } ``` `res.on('finish')` captures the status code and duration after the response completes. The middleware doesn't block the handler - it hooks into the response lifecycle and logs after the fact. ### Intermediate: request ID middleware ```typescript import { Injectable, NestMiddleware } from '@nestjs/common'; import { Request, Response, NextFunction } from 'express'; import { v4 as uuid } from 'uuid'; @Injectable() export class RequestIdMiddleware implements NestMiddleware { use(req: Request, res: Response, next: NextFunction) { // Reuse client-sent ID or generate a fresh one const requestId = (req.headers['x-request-id'] as string) || uuid(); req['requestId'] = requestId; // attach for controllers and services res.setHeader('x-request-id', requestId); // echo back to client next(); } } ``` Any controller, service, or guard running after this middleware can read `req['requestId']` for distributed tracing. The same ID appears in the response header, so the client can correlate its own logs with server logs. ### Advanced: async versioned middleware interacting with guards ```typescript // version.middleware.ts @Injectable() export class VersionMiddleware implements NestMiddleware { async use(req: Request, res: Response, next: NextFunction) { if (req.url.startsWith('/api/v2') && !req.headers['api-key']) { // Short-circuits before any guard runs return res.status(401).json({ error: 'API key required for v2 endpoints' }); } try { await validateApiKey(req.headers['api-key'] as string); // async DB/cache check next(); } catch (err) { next(err); // pass to Express error handler, not swallowed } } } // app.module.ts configure(consumer: MiddlewareConsumer) { consumer .apply(VersionMiddleware) .forRoutes({ path: 'api/v2/*', method: RequestMethod.ALL }); } ``` This middleware stops v2 requests with no API key before they reach a guard. The `JwtAuthGuard` on the controller still runs for valid requests, giving you two independent access control layers without mixing their responsibilities.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.