I'm defining a roles guard like this:
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable } from 'rxjs';
import { User } from './user.entity';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(
private reflector: Reflector,
) { }
async matchRoles(roles: string[], userRole: User["roles"]) {
let match = false;
if (roles.indexOf(userRole) > -1) {
match = true;
}
return match
}
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const roles = this.reflector.get<string[]>('roles', context.getClass());
if (!roles) {
return true;
}
const request = context.switchToHttp().getRequest();
const user: User = request.user;
return this.matchRoles(roles, user.roles)
}
}
in this roles example it works only in a controller level like this:
@Controller('games')
@hasRoles('user')
@UseGuards(AuthGuard(), JwtGuard, RolesGuard)
export class GamesController {
...
But i want it to work dynamically with both, at controller level, and handler level.
so i can appy a @hasRoles('user')
for every route in the controller, and @hasRoles('admin')
for some routes in that controller.
So to do this i need to change the reflector method from getClass
to getHandler
dynamically.
Nest's
Reflector
has a built-in method to merge the metadata set on controllers and route handlers withgetAllAndMerge
which will merge the metadata from the class and the method. To use it you would do something likeIf you wanted to get just one set of metadata and have a fallback (say if you want only the handler metadata if it exists and if not get the class's metadata) you can use
getAllAndOverride
in a similar mannerYou can read more in-depth about it here.