Is it possible to pass a parameter to a nestjs guard?

Viewed 7826

I'm trying to come up with a somewhat reusable guard and it looks like I need to pass a string param to a guard. Is it achievable in nestjs?

3 Answers

It sounds like you are looking to use a mixin, a function that returns a class. I'm not sure what kind of parameter you're passing, but the idea is

export const RoleGuard = (role: string) => {
  class RoleGuardMixin implements CanActivate {
    canActivate(context: ExecutionContext) {
      // do something with context and role
      return true;
    }
  }

  const guard = mixin(RoleGuardMixin);
  return guard;
}

mixin as a function is imported from @nestjs/common and is a wrapper function that applies the @Injectable() decorator to the class

Now to use the guard, you need to do something like @UseGuards(RoleGuard('admin'))

It seems impossible to use mixin in Guard in NestJs. It will throw Exported variable 'RoleGuard' has or is using private name 'RoleGuardMixin'.

Actually, you may use setMetadata to pass in the parameters one by one, then get it from the Guard using reflector from from '@nestjs/core'.

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

        canActivate(context: ExecutionContext) {
          const roleName = this.reflector.get<string>('roleName', context.getHandler());
          return true;
        }
    }
    @Get()
    @SetMetadata('roleName', 'developer')
    async testRoleGuard() {
      return true;
    }

Or you may define a decorator to pass the parameter in.

export const RoleName = (roleName: string) => SetMetadata('roleName', roleName);
    @Get()
    @RoleName('developer')
    async testRoleGuard() {
      return true;
    }

Usage:

@UseGuards(new AuthorizeGuard('read', 'users'))

Guard:

@Injectable()
export class AuthorizeGuard implements CanActivate {

  constructor(private action, private subject) {}

  canActivate(context: ExecutionContext): boolean {
    //you can use this.action and this.subject
  }
}
Related