Implementing role-based access control in your NestJS app? Here's a clean and scalable solution using a custom PermissionGuard that:
✅ Supports @public() routes
✅ Checks permissions via @Permission() decorator
✅ Allows super_admin to bypass all checks
✅ Supports wildcard access like user:*
🔐 Example:
@Permission('user:create')
@Post('create')
createUser() { ... }
No more messy checks inside controllers! This guard handles everything neatly and efficiently at the route level.
🧱 The Permission Guard Code
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { IS_PUBLIC_KEY } from 'src/decorators/public.decorator';
import { PERMISSION_KEY } from 'src/decorators/Permission.decorator';
import { JwtPayload } from 'jsonwebtoken';
import { AdministratorRoleEnum } from '@prisma/client';
@Injectable()
export class PermissionGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const isPublic = this.reflector.get(
IS_PUBLIC_KEY,
context.getHandler(),
);
if (isPublic) return true;
const requiredRules = this.reflector.get(
PERMISSION_KEY,
context.getHandler(),
);
if (!requiredRules || requiredRules.length === 0) return true;
const request = context.switchToHttp().getRequest();
const user = request.user as JwtPayload;
if (!user) return false;
if (user.role === AdministratorRoleEnum.super_admin) return true;
if (!Array.isArray(user.rules)) return false;
return requiredRules.every((requiredRule) => {
if (user.rules.includes(requiredRule)) return true;
const resource = requiredRule.split(':')?.[0];
return user.rules.includes(`${resource}:*`);
});
}
}
Perfect for admin panels, dashboards, or any app with multiple user roles. 💼
👉 Clean, scalable, and production-ready RBAC in NestJS.
Regards,
N I Rimon