Skip to main content
Practice Problems

How to implement Role-Based Access Control (RBAC) in NestJS?

RBAC in NestJS

Role-Based Access Control (RBAC) restricts access to resources based on user roles. NestJS provides Guards and decorators to implement flexible authorization.


1. Define Roles

typescript
export enum Role { User = 'user', Admin = 'admin', Moderator = 'moderator', SuperAdmin = 'superadmin', }

2. Roles Decorator

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

3. Roles Guard

typescript
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.getAllAndOverride<Role[]>( ROLES_KEY, [context.getHandler(), context.getClass()], ); if (!requiredRoles) return true; const { user } = context.switchToHttp().getRequest(); return requiredRoles.some((role) => user.roles?.includes(role)); } }

4. Apply Guards

typescript
@Controller('admin') @UseGuards(JwtAuthGuard, RolesGuard) export class AdminController { @Get('dashboard') @Roles(Role.Admin, Role.SuperAdmin) getDashboard() { return { message: 'Admin dashboard' }; } @Delete('users/:id') @Roles(Role.SuperAdmin) deleteUser(@Param('id') id: string) { return this.usersService.delete(id); } @Get('stats') @Roles(Role.Admin, Role.Moderator) getStats() { return this.statsService.getAll(); } }

5. Global Guard Setup

typescript
@Module({ providers: [ { provide: APP_GUARD, useClass: JwtAuthGuard }, { provide: APP_GUARD, useClass: RolesGuard }, ], }) export class AppModule {}

Advanced: Permission-Based Access

For finer control, use permissions instead of (or alongside) roles:

typescript
export enum Permission { ReadUsers = 'read:users', WriteUsers = 'write:users', DeleteUsers = 'delete:users', ReadPosts = 'read:posts', WritePosts = 'write:posts', ManageSettings = 'manage:settings', } const rolePermissions: Record<Role, Permission[]> = { [Role.User]: [Permission.ReadUsers, Permission.ReadPosts], [Role.Moderator]: [ Permission.ReadUsers, Permission.ReadPosts, Permission.WritePosts, ], [Role.Admin]: [ Permission.ReadUsers, Permission.WriteUsers, Permission.ReadPosts, Permission.WritePosts, Permission.ManageSettings, ], [Role.SuperAdmin]: Object.values(Permission), // All permissions };
typescript
export const RequirePermissions = (...permissions: Permission[]) => SetMetadata('permissions', permissions); @Injectable() export class PermissionsGuard implements CanActivate { constructor(private reflector: Reflector) {} canActivate(context: ExecutionContext): boolean { const required = this.reflector.getAllAndOverride<Permission[]>( 'permissions', [context.getHandler(), context.getClass()], ); if (!required) return true; const { user } = context.switchToHttp().getRequest(); const userPermissions = user.roles .flatMap((role: Role) => rolePermissions[role] || []); return required.every((perm) => userPermissions.includes(perm)); } }

Usage:

typescript
@Controller('settings') export class SettingsController { @Get() @RequirePermissions(Permission.ManageSettings) getSettings() { return this.settingsService.getAll(); } }

Resource Ownership Check

typescript
@Injectable() export class OwnershipGuard implements CanActivate { constructor(private readonly postsService: PostsService) {} async canActivate(context: ExecutionContext): Promise<boolean> { const request = context.switchToHttp().getRequest(); const user = request.user; const postId = request.params.id; if (user.roles.includes(Role.Admin)) return true; const post = await this.postsService.findOne(postId); return post?.authorId === user.id; } } @Controller('posts') export class PostsController { @Put(':id') @UseGuards(OwnershipGuard) update(@Param('id') id: string, @Body() dto: UpdatePostDto) { return this.postsService.update(id, dto); } }

RBAC vs ABAC

FeatureRBACABAC
Based onRolesAttributes (role, time, IP, etc.)
ComplexitySimpleComplex
FlexibilityModerateVery high
Best forMost web appsEnterprise, compliance

Best practice: Start with simple RBAC. Add permission-based checks when role granularity isn't enough. Add ownership guards for user-owned resources. Use @Public() for open endpoints.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?
Practice Problems