# How to Implement Custom Guards in NestJS: Authorization and Access Control

> Implement custom guards in NestJS to control access. Learn to define authorization logic in your canActivate method and apply guards using @UseGuards() for route or global protection.

- Repository: [nestjs/nest](https://github.com/nestjs/nest)
- Tags: how-to-guide
- Published: 2026-03-01

---

**To implement custom guards in NestJS, create an `@Injectable()` class that implements the `CanActivate` interface, define authorization logic in the `canActivate` method to return a boolean (or Promise/Observable), and apply the guard using `@UseGuards()` at the route or controller level, or `app.useGlobalGuards()` for application-wide protection.**

Guards in NestJS act as the authorization gatekeepers of the request pipeline, determining whether incoming requests proceed to route handlers based on custom logic such as roles, permissions, or feature flags. According to the nestjs/nest source code, guards are first-class injectable classes that execute **before** interceptors and route handlers, leveraging the framework's dependency injection container for complex authentication scenarios. This guide examines the actual implementation details from the repository to build production-ready authorization guards.

## Guard Architecture and Execution Flow

NestJS executes guards through a well-defined pipeline involving metadata collection, dependency resolution, and sequential activation. Understanding this flow helps debug authorization issues and optimize guard performance.

### The CanActivate Contract

Every guard must implement the `CanActivate` interface defined in [`packages/common/interfaces/features/can-activate.interface.ts`](https://github.com/nestjs/nest/blob/main/packages/common/interfaces/features/can-activate.interface.ts). The contract requires a single method:

```typescript
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean>

```

This method receives an `ExecutionContext` instance (implemented in [`packages/core/helpers/execution-context-host.ts`](https://github.com/nestjs/nest/blob/main/packages/core/helpers/execution-context-host.ts)) that provides access to the underlying HTTP request via `context.switchToHttp().getRequest()` and route handler metadata via `context.getHandler()`.

### Guard Resolution and Activation

When a request enters the pipeline, the framework executes guards through several internal components:

1. **Metadata Collection**: The `@UseGuards()` decorator stores guard constructors in metadata using the `GUARDS_METADATA` constant ([`packages/common/constants.ts`](https://github.com/nestjs/nest/blob/main/packages/common/constants.ts)).

2. **Context Creation**: `GuardsContextCreator` ([`packages/core/guards/guards-context-creator.ts`](https://github.com/nestjs/nest/blob/main/packages/core/guards/guards-context-creator.ts)) reads this metadata and resolves each guard to an instance, either instantiating new objects or retrieving them from the DI container. The `create` method (lines 22-38) handles this resolution, including global guards registered via `getGlobalMetadata` (lines 95-119).

3. **Execution Loop**: `GuardsConsumer.tryActivate` ([`packages/core/guards/guards-consumer.ts`](https://github.com/nestjs/nest/blob/main/packages/core/guards/guards-consumer.ts), lines 7-35) receives the guard array and calls `guard.canActivate(context)` sequentially. If any guard returns `false`, the request is denied immediately.

4. **Async Normalization**: The `pickResult` method (lines 49-56) handles synchronous booleans, Promises, and RxJS Observables uniformly, allowing guards to use any async pattern.

## Implementing a Custom Roles Guard

Below is a production-ready implementation that checks if an authenticated user possesses required roles. This example follows the pattern used in [`sample/36-hmr-esm/src/common/guards/roles.guard.ts`](https://github.com/nestjs/nest/blob/main/sample/36-hmr-esm/src/common/guards/roles.guard.ts).

```typescript
// src/common/guards/roles.guard.ts
import {
  CanActivate,
  ExecutionContext,
  Injectable,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Roles } from '../decorators/roles.decorator';

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

  canActivate(context: ExecutionContext): boolean {
    // Read metadata attached by the @Roles() decorator
    const requiredRoles = this.reflector.get<string[]>(Roles, context.getHandler());
    
    if (!requiredRoles?.length) {
      return true;
    }

    // Extract user from request (assumes prior auth middleware)
    const request = context.switchToHttp().getRequest();
    const user = request.user;

    // Check if user has at least one required role
    return !!user?.roles?.some(role => requiredRoles.includes(role));
  }
}

```

**Key implementation details:**

- **`@Injectable()`**: Required for NestJS to inject the `Reflector` service and other dependencies.
- **`Reflector`**: Reads metadata set by custom decorators (like `@Roles()`) using `reflect-metadata` under the hood.
- **`context.switchToHttp()`**: Provides access to the underlying Express or Fastify request object.
- **Return value**: Must resolve to a truthy value to allow the request; falsy values trigger a 403 Forbidden response.

The accompanying metadata decorator (referenced in [`sample/10-fastify/src/common/decorators/roles.decorator.ts`](https://github.com/nestjs/nest/blob/main/sample/10-fastify/src/common/decorators/roles.decorator.ts)) attaches the roles array to the route handler:

```typescript
import { SetMetadata } from '@nestjs/common';
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);

```

## Registering Guards at Different Scopes

NestJS allows guard registration at three levels of granularity, each suited to different authorization patterns.

### Route-Level Guards

Apply guards to individual handlers using the `@UseGuards()` decorator:

```typescript
import { UseGuards, Get } from '@nestjs/common';
import { RolesGuard } from '../guards/roles.guard';

@Controller('admin')
export class AdminController {
  @Roles('admin', 'manager')
  @UseGuards(RolesGuard)
  @Get('dashboard')
  getDashboard() {
    return 'Sensitive admin data';
  }
}

```

### Controller-Level Guards

To protect every route within a controller, apply the decorator at the class level:

```typescript
@UseGuards(RolesGuard)
@Controller('admin')
export class AdminController {
  // All routes inherit this guard
}

```

### Global Guards

For application-wide authorization (such as JWT validation), register guards during bootstrap:

```typescript
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { RolesGuard } from './common/guards/roles.guard';
import { Reflector } from '@nestjs/core';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  
  // Manual instantiation requires passing dependencies explicitly
  app.useGlobalGuards(new RolesGuard(app.get(Reflector)));
  
  await app.listen(3000);
}
bootstrap();

```

**Important**: When using `useGlobalGuards()` with manually instantiated guards, you must explicitly pass dependencies like `Reflector` because the global registration bypasses the automatic dependency injection that occurs with `@UseGuards()`.

## Key Source Files in the NestJS Repository

Understanding these source files helps when debugging guard behavior or extending the framework:

- **[`packages/common/interfaces/features/can-activate.interface.ts`](https://github.com/nestjs/nest/blob/main/packages/common/interfaces/features/can-activate.interface.ts)**: Defines the `CanActivate` interface contract that all guards must implement (lines 14-25).

- **[`packages/core/guards/guards-context-creator.ts`](https://github.com/nestjs/nest/blob/main/packages/core/guards/guards-context-creator.ts)**: Contains the `create` method (lines 22-38) that resolves guard metadata into instances, and `getGlobalMetadata` (lines 95-119) for handling global guard registration.

- **[`packages/core/guards/guards-consumer.ts`](https://github.com/nestjs/nest/blob/main/packages/core/guards/guards-consumer.ts)**: Implements `tryActivate` (lines 7-35) which executes the guard chain, and `pickResult` (lines 49-56) for normalizing sync/async return values.

- **[`packages/core/helpers/execution-context-host.ts`](https://github.com/nestjs/nest/blob/main/packages/core/helpers/execution-context-host.ts)**: Provides the `ExecutionContext` implementation with `switchToHttp()`, `getHandler()`, and `getClass()` methods.

- **[`sample/36-hmr-esm/src/common/guards/roles.guard.ts`](https://github.com/nestjs/nest/blob/main/sample/36-hmr-esm/src/common/guards/roles.guard.ts)**: Reference implementation of a role-based guard used in NestJS official samples.

## Summary

- **Implement `CanActivate`**: All custom guards must implement this interface and return a boolean, Promise, or Observable from `canActivate()`.
- **Use `@Injectable()`**: Enable dependency injection for services like `Reflector` or custom repositories within your guard.
- **Leverage `ExecutionContext`**: Access HTTP requests via `context.switchToHttp().getRequest()` and route metadata via `context.getHandler()`.
- **Register appropriately**: Use `@UseGuards()` for route/controller scope, or `app.useGlobalGuards()` for application-wide protection (passing dependencies manually when needed).
- **Understand the pipeline**: Guards are resolved by `GuardsContextCreator` and executed by `GuardsConsumer` before any interceptor or route handler runs.

## Frequently Asked Questions

### What is the CanActivate interface in NestJS?

The `CanActivate` interface is the contract that defines a NestJS guard, located in [`packages/common/interfaces/features/can-activate.interface.ts`](https://github.com/nestjs/nest/blob/main/packages/common/interfaces/features/can-activate.interface.ts). It requires a single `canActivate(context: ExecutionContext)` method that returns a boolean, Promise<boolean>, or Observable<boolean>. When this method returns true (or resolves to true), the request proceeds; otherwise, NestJS returns a 403 Forbidden error.

### How do I inject dependencies into a custom guard?

Decorate your guard class with `@Injectable()` and include dependencies in the constructor. NestJS's dependency injection container automatically resolves these when the guard is applied via `@UseGuards()`. However, when using `app.useGlobalGuards()` with manual instantiation (e.g., `new RolesGuard()`), you must explicitly pass dependencies using `app.get(Reflector)` or other provider retrieval methods.

### Can guards be applied globally in NestJS?

Yes, guards can be registered globally using `app.useGlobalGuards()` in your bootstrap function, or by setting `APP_GUARD` as a provider in a module. Global guards execute for every route in the application, making them ideal for authentication or universal access logging. Note that global guards run after middleware but before route-specific guards.

### How does NestJS handle asynchronous guard results?

The `GuardsConsumer` class in [`packages/core/guards/guards-consumer.ts`](https://github.com/nestjs/nest/blob/main/packages/core/guards/guards-consumer.ts) normalizes all return types through the `pickResult` method (lines 49-56), which supports plain booleans, Promises, and RxJS Observables. This allows you to implement async logic—such as database lookups or external API calls—within `canActivate()` using standard async/await or Observable patterns without additional configuration.