I want to achieve something like this using nest.js: (something very similar with Spring framework)
@Controller('/test')
class TestController {
@Get()
get(@Principal() principal: Principal) {
}
}
After hours of reading documentation, I found that nest.js supports creating custom decorator. So I decided to implement my own @Principal
decorator. The decorator is responsible for retrieving access token from http header and get principal of user from my own auth service using the token.
import { createParamDecorator } from '@nestjs/common';
export const Principal = createParamDecorator((data: string, req) => {
const bearerToken = req.header.Authorization;
// parse.. and call my authService..
// how to call my authService here?
return null;
});
But the problem is that I have no idea how to get my service instance inside a decorator handler. Is it possible? And how? Thank you in advance
A decorator is an expression that returns a function. It can take a target, name and property descriptor as arguments. We apply a decorator with an @ character and place it at the top of what we are trying to decorate. We can define decorators for class, method or a property. NestJS provides a set of param decorators.
To instruct Nestjs that we need the body values from the request, we should use the @Body() decorator function from the @nestjs/common module before the body parameter. Doing this will bind the body values from the request to the body parameter in the createPost() method.
Controllers in Nest. js are meant to receive incoming HTTP requests from an application frontend and return an appropriate response.
NestJS is an open-source, extensible, versatile, progressive Node. Js framework for creating compelling and demanding backend systems. It's currently the fastest-growing Node. Js framework in TypeScript.
It is not possible to inject a service into your custom decorator.
Instead, you can create an AuthGuard
that has access to your service. The guard can then add a property to the request
object, which you can then access with your custom decorator:
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private authService: AuthService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const bearerToken = request.header.Authorization;
const user = await this.authService.authenticate(bearerToken);
request.principal = user;
// If you want to allow the request even if auth fails, always return true
return !!user;
}
}
import { createParamDecorator } from '@nestjs/common';
export const Principal = createParamDecorator((data: string, req) => {
return req.principal;
});
and then in your controller:
@Get()
@UseGuards(AuthGuard)
get(@Principal() principal: Principal) {
// ...
}
Note that nest offers some standard modules for authentication, see the docs.
for NestJS v7
Create custom pipe
// parse-token.pipe.ts
import { ArgumentMetadata, Injectable, PipeTransform } from '@nestjs/common';
import { AuthService } from './auth.service';
@Injectable()
export class ParseTokenPipe implements PipeTransform {
// inject any dependency
constructor(private authService: AuthService) {}
async transform(value: any, metadata: ArgumentMetadata) {
console.log('additional options', metadata.data);
return this.authService.parse(value);
}
}
Use this pipe with property decorator
// decorators.ts
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
import { ParseTokenPipe} from './parse-token.pipe';
export const GetToken = createParamDecorator((data: unknown, ctx: ExecutionContext) => {
return ctx.switchToHttp().getRequest().header.Authorization;
});
export const Principal = (additionalOptions?: any) => GetToken(additionalOptions, ParseTokenPipe);
Use this decorator with or without additional options
@Controller('/test')
class TestController {
@Get()
get(@Principal({hello: "world"}) principal) {}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With