Good Code
The good version uses middleware for request metadata and a guard for authorization. Each concern maps to a lifecycle stage.
Lesson 09
Place middleware, guards, pipes, and interceptors according to the NestJS request lifecycle.
import {
CanActivate,
ExecutionContext,
Injectable,
NestMiddleware,
} from "@nestjs/common";
import type { NextFunction, Response } from "express";
@Injectable()
export class RequestContextMiddleware implements NestMiddleware {
// Middleware attaches low-level request metadata.
use(request: RequestWithContext, _response: Response, next: NextFunction) {
request.requestId = crypto.randomUUID();
next();
}
}
@Injectable()
export class AdminGuard implements CanActivate {
canActivate(context: ExecutionContext) {
const request = context.switchToHttp().getRequest<RequestWithContext>();
return request.user?.role === "admin";
}
}import { Injectable, NestMiddleware } from "@nestjs/common";
@Injectable()
export class AuthMiddleware implements NestMiddleware {
use(request: any, response: any, next: () => void) {
// Access policy hidden in middleware can bypass controller metadata.
if (request.path.startsWith("/admin") && request.user?.role !== "admin") {
response.status(403).send("Forbidden");
return;
}
next();
}
}The good version uses middleware for request metadata and a guard for authorization. Each concern maps to a lifecycle stage.
The bad version hides route access policy inside path-string checks. Controller metadata and route decorators no longer tell the full security story.