Reputation: 2664
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?
Upvotes: 30
Views: 23650
Reputation: 399
Update on the accepted answer by Jay
For NestJS 9 users: If you encounter a reference error, make sure to add @Injectable() to your Mixin class. This should resolve the issue.
export const RoleGuard = (role: string) => {
@Injectable() // => Here
class RoleGuardMixin implements CanActivate {
canActivate(context: ExecutionContext) {
// do something with context and role
return true;
}
}
const guard = mixin(RoleGuardMixin);
return guard;
}
Upvotes: 1
Reputation: 241
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()
@UseGuards(RoleGuard)
@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()
@UseGuards(RoleGuard)
@RoleName('developer')
async testRoleGuard() {
return true;
}
UseGuards
is imported from @nestjs/common
.
Upvotes: 21
Reputation: 45
If anyone is struggling with testing the mixin approach @JayMcDoniel brilliantly pointed out, here's a sample setup from a guard I implemented using the mixin.
describe('PaymentGuard', () => {
let guard: CanActivate;
beforeEach(async () => {
const guardProvider = PaymentGuard(PaymentGuardHandlerToken.EXPECT_PLATFORM_ENABLED);
const module = await Test.createTestingModule({
providers: [
guardProvider,
{
provide: PaymentService,
useValue: MOCK_PAYMENT_SERVICE,
},
{
provide: EntityManager,
useValue: MOCK_ENTITY_MANAGER,
}
],
}).compile();
guard = module.get(guardProvider);
});
});
And then you just call the guard.canActivate
within your test.
Upvotes: 1
Reputation: 762
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
}
}
Upvotes: -2
Reputation: 70061
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'))
Upvotes: 61