In my nest app, I try to read a route's decorator in a custom guard, using the getAllAndOverride function from Reflector (basically follwing documentation and best practice I hope). When I extend the custom guard from the Passport 'AuthGuard', the reflector object exists, but is not an instance of Reflector (I do not know what kind of object it is). When I create a custom guard using only the CanActivate interface, the reflector is undefined. In both cases I get the error trying to use the getAllAndOverride function.
Searching the internet gave me no solution, so I decided to start with a new, basic project, called test-guard. The only thing I changed was the follwing:
I introduced a custom decorator using a new file (public.decorator.ts):
import { SetMetadata } from '@nestjs/common';
export const PUBLIC_KEY = 'isPublic';
export const Public = () => SetMetadata(PUBLIC_KEY, true);
I introduced a custom guard using a new file (test-guard.ts):
import { Reflector } from '@nestjs/core';
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Observable } from 'rxjs';
import { PUBLIC_KEY } from './public.decorator';
Injectable()
export class TestGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean | Promise<boolean> | Observable<boolean> {
console.log(typeof(this.reflector));
console.log(this.reflector instanceof Reflector);
const isPublic = this.reflector.getAllAndOverride(PUBLIC_KEY, [
context.getHandler(),
context.getClass(),
]);
if (isPublic) return true;
return false;
}
}
And in my AppModule I introduced a global guard using a provider:
{
provide: APP_GUARD,
useClass: TestGuard,
},
That's it. And I got the same problem. this.reflector in my TestGuard was undefined.
Commenting out the section regarding 'isPublic' made the guard work: returning true showed me 'Hello World', returning false gave me a 403 (Forbidden). Perfect. However with the 'isPublic' section active I got the error:
[12:00:55] File change detected. Starting incremental compilation...
[12:00:55] Found 0 errors. Watching for file changes.
[Nest] 7844 - 14-01-2024 12:00:55 LOG [NestFactory] Starting Nest application...
[Nest] 7844 - 14-01-2024 12:00:55 LOG [InstanceLoader] AppModule dependencies initialized +8ms
[Nest] 7844 - 14-01-2024 12:00:55 LOG [RoutesResolver] AppController {/}: +12ms
[Nest] 7844 - 14-01-2024 12:00:55 LOG [RouterExplorer] Mapped {/, GET} route +2ms
[Nest] 7844 - 14-01-2024 12:00:55 LOG [NestApplication] Nest application successfully started +2ms
undefined
false
[Nest] 7844 - 14-01-2024 12:00:57 ERROR [ExceptionsHandler] Cannot read properties of undefined (reading 'getAllAndOverride')
TypeError: Cannot read properties of undefined (reading 'getAllAndOverride')
at TestGuard.canActivate (C:\wamp64\www\test-guard\src\test-guard.ts:14:41)
at GuardsConsumer.tryActivate (C:\wamp64\www\test-guard\node_modules\@nestjs\core\guards\guards-consumer.js:15:34)
at canActivateFn (C:\wamp64\www\test-guard\node_modules\@nestjs\core\router\router-execution-context.js:134:59)
at C:\wamp64\www\test-guard\node_modules\@nestjs\core\router\router-execution-context.js:42:37
at C:\wamp64\www\test-guard\node_modules\@nestjs\core\router\router-proxy.js:9:23
at Layer.handle [as handle_request] (C:\wamp64\www\test-guard\node_modules\express\lib\router\layer.js:95:5)
at next (C:\wamp64\www\test-guard\node_modules\express\lib\router\route.js:144:13)
at Route.dispatch (C:\wamp64\www\test-guard\node_modules\express\lib\router\route.js:114:3)
at Layer.handle [as handle_request] (C:\wamp64\www\test-guard\node_modules\express\lib\router\layer.js:95:5)
at C:\wamp64\www\test-guard\node_modules\express\lib\router\index.js:284:15
The strange thing is, that when I comment out the new provider in my AppModule, and instead use
app.useGlobalGuards(new TestGuard(new Reflector()),);
in my main.ts, the guard works as expected! When I read the documentation however, it says:
Global guards are used across the whole application, for every controller and every route handler. In terms of dependency injection, global guards registered from outside of any module (with useGlobalGuards() as in the example above) cannot inject dependencies since this is done outside the context of any module. In order to solve this issue, you can set up a guard directly from any module using the following construction
followed by the construction I used in the AppModule, using the provider.
So my interpretation is that for a reflector to be injected in the guard, I should use my original solution. However, my test app works when I use the useGlobalGuards in my main.ts. I fear that when I start injecting other services in my guard it will no longer work using useGlobalGuards.
Could anybody tell me what I'm missing? I'm no expert in developping, but I was starting to get a grip on the nestjs structure (at least that's what I thought), but this I do not get ...