Role-based authorization helps control who can do what in your app. It keeps things safe by letting only certain users access specific parts.
Role-based authorization in NestJS
import { Injectable, CanActivate, ExecutionContext } 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.getAllAndOverride<string[]>('roles', [ context.getHandler(), context.getClass(), ]); if (!requiredRoles) { return true; } const { user } = context.switchToHttp().getRequest(); return requiredRoles.some(role => user.roles?.includes(role)); } }
The RolesGuard checks if the user has the needed roles to access a route.
Use @SetMetadata('roles', ['roleName']) on controllers or routes to specify roles.
import { SetMetadata } from '@nestjs/common'; export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
@Roles('admin') @Get('admin-data') getAdminData() { return 'Secret admin data'; }
@UseGuards(RolesGuard) @Controller('users') export class UsersController { @Roles('user', 'admin') @Get('profile') getProfile() { return 'User profile data'; } }
This example shows a controller with two routes. One route is only for admins, the other for users or admins. The RolesGuard checks the user's roles before allowing access.
import { Controller, Get, UseGuards } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { Injectable, CanActivate, ExecutionContext, 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.getAllAndOverride<string[]>('roles', [ context.getHandler(), context.getClass(), ]); 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 the admin dashboard'; } @Roles('user', 'admin') @Get('user') getUserDashboard() { return 'Welcome to the user dashboard'; } }
Make sure your authentication system adds a user object with roles to the request.
Roles are usually strings like 'admin', 'user', or 'editor'.
Use @UseGuards(RolesGuard) to activate role checks on controllers or routes.
Role-based authorization controls access by user roles.
Use decorators to mark which roles can access routes.
Use a guard to check roles before allowing access.
Practice
Solution
Step 1: Understand role-based authorization concept
Role-based authorization restricts access to parts of an app depending on the user's role.Step 2: Identify the purpose in NestJS context
In NestJS, it controls which users can access certain routes or features based on their roles.Final Answer:
To control access to routes based on user roles -> Option CQuick Check:
Role-based authorization = Access control by roles [OK]
- Confusing authorization with authentication
- Thinking it manages database or HTTP parsing
- Mixing up roles with user data formatting
Solution
Step 1: Identify role-specifying decorators
In NestJS, @Roles() decorator marks which roles can access a route.Step 2: Differentiate from other decorators
@UseGuards() applies guards, @Controller() defines controllers, @Get() defines HTTP GET routes.Final Answer:
@Roles() -> Option AQuick Check:
@Roles() sets allowed roles [OK]
- Using @UseGuards() to specify roles directly
- Confusing route decorators with role decorators
- Missing that @Roles() is custom, not built-in
@Roles('admin')
@Get('admin-data')
getAdminData() {
return 'Secret admin data';
}Solution
Step 1: Check the role required by the route
The route requires 'admin' role via @Roles('admin').Step 2: Compare with user's role
User has role 'user', which does not match 'admin'.Final Answer:
The user will get a 403 Forbidden error -> Option BQuick Check:
Role mismatch causes 403 Forbidden [OK]
- Assuming any role can access
- Thinking route returns undefined on denial
- Expecting server crash on role mismatch
canActivate(context: ExecutionContext): boolean {
const request = context.switchToHttp().getRequest();
const user = request.user;
const roles = this.reflector.get('roles', context.getHandler());
if (!roles) {
return true;
}
return roles.includes(user.role);
}Solution
Step 1: Review user role access
The code accesses user.role without verifying if user is defined.Step 2: Identify potential runtime error
If user is undefined, accessing user.role causes an error.Final Answer:
Not checking if user exists before accessing user.role -> Option DQuick Check:
Always check user before role access [OK]
- Assuming user is always present
- Confusing getHandler() and getClass() usage
- Thinking async is required for synchronous guard
Solution
Step 1: Understand role decorator usage
@Roles() accepts multiple roles to allow any of them access.Step 2: Use a guard that checks if user role matches any allowed role
The RolesGuard reads roles from metadata and checks if user role is included.Final Answer:
Use @Roles('admin', 'manager') decorator and a RolesGuard that checks for any matching role -> Option AQuick Check:
Multiple roles in @Roles() + guard = flexible access [OK]
- Using multiple @Roles decorators on one route
- Applying multiple guards instead of one flexible guard
- Skipping decorators and guards for manual checks
Ready to prove it?
Pass the test and this pattern moves unknown
