Role-based guards help control who can access certain parts of your app based on their role. They keep your app safe by allowing only the right users to do specific actions.
Role-based guards in NestJS
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; @Injectable() export class RolesGuard implements CanActivate { constructor(private reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { const requiredRoles = this.reflector.get<string[]>('roles', context.getHandler()); if (!requiredRoles) { return true; } const { user } = context.switchToHttp().getRequest(); return requiredRoles.some(role => user.roles?.includes(role)); } }
The guard uses Reflector to read roles metadata set on route handlers.
The canActivate method returns true if the user has any required role.
@Roles decorator.@Roles('admin') @Get('admin') getAdminData() { return 'Admin data'; }
@Roles decorator that attaches roles metadata to routes.import { SetMetadata } from '@nestjs/common'; export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
RolesGuard protects the getProfile route, allowing only users with 'user' or 'admin' roles.@UseGuards(RolesGuard) @Controller('users') export class UsersController { @Roles('user', 'admin') @Get('profile') getProfile() { return 'User profile'; } }
This example shows a simple NestJS controller with two routes protected by the RolesGuard. Only users with the 'admin' role can access the admin dashboard, and only users with the 'user' role can access the user dashboard.
import { Controller, Get, UseGuards } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { CanActivate, ExecutionContext, Injectable, SetMetadata } from '@nestjs/common'; export const Roles = (...roles: string[]) => SetMetadata('roles', roles); @Injectable() export class RolesGuard implements CanActivate { constructor(private reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { const requiredRoles = this.reflector.get<string[]>('roles', context.getHandler()); if (!requiredRoles) { return true; } const { user } = context.switchToHttp().getRequest(); return requiredRoles.some(role => user.roles?.includes(role)); } } @UseGuards(RolesGuard) @Controller('dashboard') export class DashboardController { @Roles('admin') @Get('admin') getAdminDashboard() { return 'Welcome to Admin Dashboard'; } @Roles('user') @Get('user') getUserDashboard() { return 'Welcome to User Dashboard'; } }
Make sure your authentication system adds a user object with roles to the request.
Use the @Roles decorator to specify which roles can access a route.
Guards run before route handlers to block unauthorized access early.
Role-based guards control access by checking user roles before running route code.
Use a custom @Roles decorator to mark routes with allowed roles.
Implement canActivate in a guard to allow or deny access based on roles.
Practice
Solution
Step 1: Understand role-based guard function
Role-based guards check user roles before allowing access to routes.Step 2: Identify the correct purpose
Restricting access based on roles matches the guard's role in security.Final Answer:
To restrict access to routes based on user roles -> Option DQuick Check:
Role-based guard = restrict access by roles [OK]
- Confusing guards with database or config management
- Thinking guards format responses
- Assuming guards handle unrelated tasks
Solution
Step 1: Identify correct guard usage syntax
Use @UseGuards with the guard class to apply it to routes.Step 2: Match with role-based guard usage
@UseGuards(RolesGuard) is correct; @Roles decorator marks roles separately.Final Answer:
@UseGuards(RolesGuard)\n@Get('profile') -> Option AQuick Check:
@UseGuards(RolesGuard) applies guard [OK]
- Using @RolesGuard() instead of @UseGuards
- Mixing @Roles and @UseGuards incorrectly
- Applying guards without @UseGuards decorator
canActivate(context: ExecutionContext) {
const roles = this.reflector.get('roles', context.getHandler());
const request = context.switchToHttp().getRequest();
const user = request.user;
return roles.includes(user.role);
}Solution
Step 1: Understand role check logic
The guard checks if the user's role is included in the allowed roles array.Step 2: Apply given roles and user role
User role is 'user', route requires 'admin', so roles.includes('user') is false.Final Answer:
Access denied -> Option BQuick Check:
User role not in allowed roles = deny access [OK]
- Assuming access is granted if roles differ
- Thinking roles includes user role always
- Ignoring roles array presence
canActivate(context: ExecutionContext) {
const roles = this.reflector.get('roles', context.getHandler());
const request = context.switchToHttp().getRequest();
const user = request.user;
if (!roles) {
return false;
}
return roles.includes(user.role);
}Solution
Step 1: Analyze roles metadata check
If roles metadata is missing, the guard returns false blocking access.Step 2: Understand typical behavior for missing roles
Usually, missing roles means no restriction, so returning false is too strict.Final Answer:
Returns false if no roles metadata, blocking all access -> Option AQuick Check:
No roles metadata should allow access, not block [OK]
- Assuming missing roles means deny access
- Forgetting to handle undefined roles gracefully
- Confusing async necessity in this context
Solution
Step 1: Understand multiple roles in @Roles decorator
@Roles can accept multiple roles as arguments to specify allowed roles.Step 2: Implement canActivate to check inclusion
Checking if user.role is in roles array allows access if user has any allowed role.Final Answer:
Use @Roles('admin', 'editor') decorator and check if user.role is included in roles array inside canActivate -> Option CQuick Check:
Check user role in roles array for multiple allowed roles [OK]
- Checking only first role ignores others
- Using multiple guards unnecessarily
- Allowing all authenticated users ignores roles
Ready to prove it?
Pass the test and this pattern moves unknown
