How to pass reflector to Nest.js global guard?
Asked Answered
T

5

7

I am new to nest.js and I have a question. I have a Roles Guard like this

import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
import { Reflector } from '@nestjs/core';

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

  canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
    const roles = this.reflector.get<string[]>('roles', context.getHandler());
    if (!roles) {
      return true;
    }
    const request = context.switchToHttp().getRequest();
    const user = request.user;
    return user.role.some(role => !!roles.find(item => item === role));
  }

}

Now I want to use this guard as a global guard like this

app.useGlobalGuards(new RolesGuard())

But it says that I need to pass argument(the reflector) to the guard as I mentioned in the constructor, now will it be okay to initialize the reflector like this?

const reflector:Reflector = new Reflector();
app.useGlobalGuards(new RolesGuard(reflector))

Or is there a better way to do this?

Tuft answered 20/4, 2020 at 17:30 Comment(0)
D
4

On the official Nest JS fundamentals course, in lecture "54 Protect Routes with Guards", the instructor specifies it is not best practice to create instance of reflector yourself.

A better way to resolve dependencies is to create a common module, and register your guard there. That way, reflector instance is resolved by nest runtime and you can also specify imports array for any other dependencies.

import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
import { AuthTokenGuard } from './guards/auth-token.guard';
import { ConfigModule } from '@nestjs/config';

@Module({
  imports: [ConfigModule],
  providers: [
    {
      provide: APP_GUARD,
      useClass: AuthTokenGuard,
    },
  ],
})
export class CommonModule {}
Demasculinize answered 16/12, 2021 at 21:48 Comment(0)
R
2
  app.useGlobalGuards(new RolesGuard(new Reflector()));

It is working also. Could not find any better solution.

Radiography answered 6/10, 2020 at 8:52 Comment(1)
SulsDotK's answer is the best practice now which is in the official guide. Another way is app.useGlobalGuards(new AuthGuard(app.get(Reflector)));Lailalain
S
1

Although my answer might not add much value, I just want to re-iterate that is the intended way to get the reflector, this is a quote from NestJS's creator

kamilmysliwiec

When you create instance manually, you can create Reflector by yourself:


new RoleGuard(new Reflector());

Source: https://github.com/nestjs/nest/issues/396#issuecomment-363111707

Spitz answered 20/11, 2020 at 12:15 Comment(0)
P
0

2023, NestJs 9, related problem:

In case you inject request-scoped dependency into the globally registered guard, the reflector will be undefined.

You can solve this issue by resolving such dependencies using ContextIdFactory and moduleRef.resolve() instead of injecting them normally:

const req = context.switchToHttp().getRequest();
const contextId = ContextIdFactory.getByRequest(req);
this.moduleRef.registerRequestByContextId(req, contextId);
this.authorizationService = await this.moduleRef.resolve(
  RequestScopedService,
  contextId
);

References:

Prosthesis answered 6/1, 2023 at 19:58 Comment(0)
G
0

I had two different versions of @nestjs/core installed, npm ls @nestjs/core told me which dependencies caused this issue.

After resolving, everything worked as expected.

Gilles answered 22/8, 2023 at 15:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.