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.

Click here to Read more

Regards,
N I Rimon