'Guard factory in nestJS application
I'm trying to create a guard factory in order to create a guard based on given params and ran into an issue.
I built the following factory
@Injectable()
export class GateKeeperFactory {
public static guards = []
static forRoute(allow: boolean) {
@Injectable()
class GateKeeperCustomGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
return allow
}
}
const result = GateKeeperCustomGuard
GateKeeperFactory.guards.push(result)
return result
}
}
and added the following to my app.module.ts
providers: [GateKeeperFactory, ...GateKeeperFactory.guards],
To test this I created the following controllers
@Controller('/test1')
export class Test1Controller {
@Get('1')
@UseGuards(GateKeeperFactory.forRoute(false))
test1() {
return { in: true }
}
@Get('2')
@UseGuards(GateKeeperFactory.forRoute(true))
test2() {
return { in: true }
}
}
@UseGuards(GateKeeperFactory.forRoute(false))
@Controller('/test2')
export class Test2Controller {
@Get('1')
test1() {
return { in: true }
}
}
The issue is that all 3 routes are either blocked or all 3 are unblocked
I can assume that the allow
param is shared between the guards for some reason I'm missing (I verified that 3 different guards are created)
Note 1:
according to the guide I know they offer a different approach of passing parameters to guards by using SetMetadata
which at this point I want to avoid
Note 2:
Trying to add a constructor to the guards and using them as instances led to the error: [ExceptionHandler] metatype is not a constructor
when loading the server
Solution 1:[1]
It sounds like what you're really looing for is what's called a mixin, a function that returns a class with a closure that allows the class to use the mixin's parameters. This is how Nest creates it's Passport AuthGuard()
, btw. To take from your example above and tweak it a bit, you'd do something like this:
import { CanActivate, ExecutionContext, mixin } from '@nestjs/common';
export function GateKeeper(allow: boolean) {
class GateKeeperCustomGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean {
return allow
}
}
return mixin(GateKeeperCustomGuard) as CanActivate;
}
And now you should be able to use the guard like @UseGuards(GateKeeper(true))
Solution 2:[2]
Just an update to OP's 2nd note: "Trying to add a constructor to the guards and using them as instances led to the error: [ExceptionHandler] metatype is not a constructor when loading the server"
The exported mixin guard must be a function declared with the function
keyword, and not an arrow function.
In order to fix this, replace
export const BlaBlaGuard = (params: any) => { /* Guard class goes here */ };
with
export function BlaBlaGuard(params: any) { /* Guard class goes here */ };
in your code, and it should fix the issue.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Jay McDoniel |
Solution 2 | Okie |