CodePark/ questions / Guards & auth
START PRACTICINGPRACTICE
Backend question · NestJS › Guards & auth

Build role-based access control with a @Roles() decorator and a guard that reads it with Reflector.

export const Roles = Reflector.createDecorator<string[]>();

@Injectable()
export class RolesGuard implements CanActivate {
  constructor(private reflector: Reflector) {}

  canActivate(ctx: ExecutionContext): boolean {
    const roles = this.reflector.getAllAndOverride(Roles, [ctx.getHandler(), ctx.getClass()]);
    if (!roles) return true;
    const { user } = ctx.switchToHttp().getRequest();
    return roles.some((r) => user?.roles?.includes(r));
  }
}

@Roles(["admin"]) @Get() findAll() {}

Create a @Roles() decorator that stores the required roles as metadata, then write a guard that reads that metadata and checks the user.

Step 1: the decorator. Reflector.createDecorator<string[]>() creates a typed decorator. It stores metadata on a handler or a class. The older way is to wrap SetMetadata("roles", roles) in your own decorator.

Step 2: the guard. The guard injects Reflector. It reads the metadata from the current handler and class through the ExecutionContext. There are two ways to read it:

  • getAllAndOverride returns the handler's value if it exists. Otherwise it returns the class's value.
  • getAllAndMerge combines the handler's and the class's values.

If no roles are set, the route is open to any logged-in (authenticated) user. Otherwise the guard compares the required roles with req.user.roles. An authentication guard set req.user earlier.

Step 3: the order. The authentication guard must run before RolesGuard, because RolesGuard needs req.user. For example, use @UseGuards(AuthGuard, RolesGuard). Or register both globally with APP_GUARD.

  • Use Reflector.createDecorator or SetMetadata to attach roles to a route.
  • The guard injects Reflector and reads the handler and class metadata.
  • getAllAndOverride prefers the handler's value, while getAllAndMerge combines both.
  • The guard compares the roles with req.user, which the auth guard set.
  • The auth guard must run before the roles guard.