Guards determine whether a given request should be handled by the route handler at all, most commonly used for authentication and authorization checks. This lesson covers the CanActivate interface and applying guards at different scopes.
What Is a Guard?
A guard is a class implementing the CanActivate interface with a single canActivate() method that returns a boolean (or a Promise/Observable resolving to one). If it returns true, the request proceeds; if false, Nest automatically throws a 403 Forbidden.
@UseGuards() can be applied to a single route, an entire controller, or globally.
Guards run after middleware but before interceptors and pipes.
Multiple guards can be combined, all must return true for the request to proceed.
Globally registered guards via useGlobalGuards() cannot use dependency injection directly, register them as providers instead for DI support.
Guards Cheatsheet
How and where to apply NestJS guards.
Scope
Syntax
Route-level
@UseGuards(SomeGuard) above a method
Controller-level
@UseGuards(SomeGuard) above a class
Global (in main.ts)
app.useGlobalGuards(new SomeGuard())
Global (DI-enabled)
{ provide: APP_GUARD, useClass: SomeGuard }
Registering a Global Guard With Dependency Injection
Calling app.useGlobalGuards() directly in main.ts bypasses Nest's DI container for that guard's own dependencies. Registering the guard as a provider using the APP_GUARD token instead keeps it fully DI-enabled.
import { APP_GUARD } from '@nestjs/core';
@Module({
providers: [{ provide: APP_GUARD, useClass: ApiKeyGuard }],
})
export class AppModule {}
Guards vs. Middleware
Unlike middleware, guards have access to the full ExecutionContext, including route handler metadata set by custom decorators (like @Roles()), making them the right tool for authorization decisions that depend on route-specific configuration.
Common Mistakes
Putting authorization logic in middleware instead of a guard, losing access to route metadata.
Registering a global guard with useGlobalGuards() when it needs injected dependencies.
Forgetting that a guard returning false produces a generic 403, not a custom error message, without extra handling.
Stacking too many unrelated checks into a single guard instead of composing smaller, focused guards.
Key Takeaways
Guards implement CanActivate and decide whether a request is allowed to proceed.
Guards can be applied at the route, controller, or global level.
Global guards needing dependency injection should be registered via the APP_GUARD token.
Guards have access to route metadata, making them ideal for role- and permission-based checks.
Pro Tip
Keep each guard focused on one concern (is the user authenticated? does the user have this role?) and compose multiple small guards with @UseGuards(AuthGuard, RolesGuard) rather than writing one large guard that does everything.
You now understand NestJS guards. Next, apply this to real authentication logic with Auth Guards.