Mastering Guards in NestJS: Handling Authorisation and Request Control

Muhire JosuéMuhire Josué
3 min read

In a real-world application, controlling access to routes and resources is critical. NestJS Guards provide a clean, declarative way to implement authorization, authentication, and request validation logic before a request reaches your route handlers.

In this guide, we’ll explore what Guards are, how they work, and how you can build both simple and dynamic Guards to secure your NestJS applications.

2. Creating a Simple Guard

Let’s create a simple authentication guard.

Step 1: Auth Guard (auth.guard.ts)

import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from '@nestjs/common';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    if (!request.headers.authorization) {
      throw new UnauthorizedException('Authorization header missing');
    }
    return true;
  }
}
  • ExecutionContext gives access to the request, response, and other context details.

  • You can throw exceptions like UnauthorizedException to block the request.


3. Applying Guards

You can apply Guards at three levels:

3.1 Method Level

@UseGuards(AuthGuard)
@Get('profile')
getProfile() {
  return { message: 'This is a protected route' };
}

3.2 Controller Level

@UseGuards(AuthGuard)
@Controller('users')
export class UserController { ... }

3.3 Global Level (main.ts)

app.useGlobalGuards(new AuthGuard());

Applying globally means all routes will pass through the Guard.

4. Building a Role-Based Authorization Guard

Let’s build a more dynamic guard that checks for user roles.

Step 1: Define Roles Metadata

Create a custom decorator roles.decorator.ts:

import { SetMetadata } from '@nestjs/common';

export const Roles = (...roles: string[]) => SetMetadata('roles', roles);

This attaches metadata to route handlers.

Step 2: Create RolesGuard

import { CanActivate, ExecutionContext, Injectable, ForbiddenException } from '@nestjs/common';
import { Reflector } from '@nestjs/core';

@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 request = context.switchToHttp().getRequest();
    const user = request.user;

    if (!user || !roles.includes(user.role)) {
      throw new ForbiddenException('You do not have access to this resource');
    }
    return true;
  }
}
  • Reflector is a helper to read metadata set by decorators.

  • This Guard checks if the user has one of the required roles.

Step 3: Usage Example

@Roles('admin')
@UseGuards(RolesGuard)
@Delete('remove/:id')
removeUser(@Param('id') id: string) {
  return this.userService.remove(id);
}

5. Accessing Request Details Inside Guards

Guards are very flexible. You can:

  • Access headers, params, body

  • Read cookies

  • Check JWT tokens

  • Integrate third-party authentication providers (e.g., OAuth)

const request = context.switchToHttp().getRequest();
const token = request.cookies['auth-token'];

6. Best Practices for Guards

  • Keep Guards small and focused — one Guard per concern (e.g., authentication vs. authorization).

  • Combine Guards where necessary using @UseGuards(Guard1, Guard2).

  • Prefer using decorators (@Roles, @Public) to control routes declaratively.

  • Throw specific exceptions (UnauthorizedException, ForbiddenException) for clarity.

Conclusion

Guards are essential for building secure, resilient NestJS applications. By controlling access before requests hit your business logic, you can cleanly enforce authentication, authorization, and custom rules at the framework level.

Whether you're protecting admin routes, implementing role-based access control, or integrating with external auth providers, Guards give you the flexibility you need to maintain a clean and scalable

0
Subscribe to my newsletter

Read articles from Muhire Josué directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Muhire Josué
Muhire Josué

I am a backend developer, interested in writing about backend engineering, DevOps and tooling.