Lesson 06-NestJS Guards

Guards

Guards in NestJS v10 are a crucial feature for controlling access to controller methods. They allow you to check specific conditions before a controller method is executed, determining whether the request should proceed.

Introduction to Guards

Guards are a mechanism in NestJS used to perform checks before a controller method is invoked. They are commonly used for authentication, authorization, role-based access control, and similar scenarios.

Creating Guards

To create a guard, you need to implement the CanActivate interface.

// src/guards/auth.guard.ts
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    return request.isAuthenticated(); // Assumes an isAuthenticated method to check if the user is logged in
  }
}

Using Guards

Guards can be applied at the method level or globally across the application.

// src/controllers/users.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from './guards/auth.guard';

@Controller('users')
export class UsersController {
  @Get()
  @UseGuards(AuthGuard)
  findAll() {
    // Only requests passing the AuthGuard check will reach here
    return 'Only authenticated users can see this.';
  }
}

Global Guards

You can set a global guard to avoid declaring guards for each controller or method.

// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { AuthGuard } from './guards/auth.guard';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalGuards(new AuthGuard());
  await app.listen(3000);
}
bootstrap();

Local Guards

Local guards can be applied to specific controllers or methods.

// src/controllers/users.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from './guards/auth.guard';

@Controller('users')
export class UsersController {
  @Get()
  @UseGuards(AuthGuard)
  findAll() {
    // Only requests passing the AuthGuard check will reach here
    return 'Only authenticated users can see this.';
  }
}

Multiple Guards

You can apply multiple guards to a single controller method.

// src/controllers/admin.controller.ts
import { Controller, Get, UseGuards } from '@nestjs/common';
import { AuthGuard } from './guards/auth.guard';
import { RoleGuard } from './guards/role.guard';

@Controller('admin')
export class AdminController {
  @Get()
  @UseGuards(AuthGuard, RoleGuard)
  index() {
    return 'Admin page';
  }
}

Dependency Injection in Guards

Guards can leverage dependency injection to access other services as needed.

// src/guards/role.guard.ts
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { ROLES_KEY } from './roles.decorator';

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

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

Roles Decorator

To use a role-based guard, you need to create a roles decorator to specify which roles can access a particular controller method.

// src/decorators/roles.decorator.ts
import { SetMetadata } from '@nestjs/common';

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

Using the Roles Decorator

You can now use the roles decorator on controller methods to specify which roles are allowed.

// src/controllers/admin.controller.ts
import { Controller, Get, UseGuards, Roles } from '@nestjs/common';
import { AuthGuard } from './guards/auth.guard';
import { RoleGuard } from './guards/role.guard';
import { ROLES_KEY } from './decorators/roles.decorator';

@Controller('admin')
export class AdminController {
  @Get()
  @UseGuards(AuthGuard, RoleGuard)
  @Roles('admin')
  index() {
    return 'Admin page';
  }
}

Error Handling in Guards

When a guard blocks a request, you can customize the error response.

// src/guards/auth.guard.ts
import { Injectable, CanActivate, ExecutionContext, UnauthorizedException } from '@nestjs/common';

@Injectable()
export class AuthGuard implements CanActivate {
  canActivate(context: ExecutionContext): boolean {
    const request = context.switchToHttp().getRequest();
    if (!request.isAuthenticated()) {
      throw new UnauthorizedException('You are not authenticated.');
    }
    return true;
  }
}
Share your love