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.