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:
getAllAndOverridereturns the handler's value if it exists. Otherwise it returns the class's value.getAllAndMergecombines 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.createDecoratororSetMetadatato attach roles to a route. - The guard injects
Reflectorand reads the handler and class metadata. getAllAndOverrideprefers the handler's value, whilegetAllAndMergecombines both.- The guard compares the roles with
req.user, which the auth guard set. - The auth guard must run before the roles guard.
- Guards (https://docs.nestjs.com/guards)docs.nestjs.com