intermediate

NestJS guards

Use guards for authentication and authorization decisions with metadata, execution contexts, and explicit denial behavior.

Guards implement `CanActivate` and run before route handlers — they answer "can this request proceed?" Authentication and authorization belong here, not in controllers or services.

					@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(context: ExecutionContext): boolean {
    const roles = this.reflector.get<string[]>('roles', context.getHandler());
    if (!roles) return true;
    const { user } = context.switchToHttp().getRequest();
    return roles.some((role) => user?.roles?.includes(role));
  }
}
				

Apply guards with `@UseGuards(AuthGuard, RolesGuard)` at controller or handler level. `@SetMetadata('roles', ['admin'])` pairs with custom guards. `ExecutionContext` abstracts HTTP, RPC, and WebSocket transports.

On interviews: guards versus middleware (middleware lacks DI and route metadata), JWT validation flow, and explicit denial (throw `ForbiddenException`).

Common pitfalls: authorization logic duplicated in services, guards that perform heavy I/O synchronously, and missing guard on new endpoints.

The trade-off is balancing simplicity, performance, safety, and operability — name which axis you optimized and what cost you accepted.

Checklist:

  • Use guards for authn/authz decisions.
  • Combine metadata with Reflector for roles.
  • Throw framework exceptions on denial.
  • Keep guards fast; cache policy lookups when needed.