You can use a combination of Decorator and Pipe to safely retrieve and transform data to what you need, for instance getting the user authenticated using a JSON Web Token.
Here is what it looks like in use in a controller.
// profile.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AuthenticationToken } from 'src/authentication/authentication.decorator';
import { UserEntity } from 'src/users/users.entity';
import { UserFromTokenPipe } from 'src/users/users.pipe';
@Controller('profile')
export class ProfileController {
@Get()
public getProfile(@AuthenticationToken(UserFromTokenPipe) user: UserEntity) {
const { email, firstname, lastname } = user;
return {
email,
firstname,
lastname
}
}
}
Here is what it looks like in the decorator.
// authentication.decorator.ts
import { createParamDecorator, ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { Request } from 'express';
export const AuthenticationToken = createParamDecorator((_data: unknown, context: ExecutionContext) => {
const request = context.switchToHttp().getRequest<Request>();
const authorizationToken = request.headers.authorization;
if (!authorizationToken) {
throw new UnauthorizedException("Missing authorization token");
}
const [bearer, token] = authorizationToken.split(' ');
if (bearer !== 'Bearer') {
throw new UnauthorizedException("Invalid authorization token type");
}
return token;
});
And here is what it looks like in the pipe.
// users.pipe.ts
import { ArgumentMetadata, Injectable, PipeTransform, UnauthorizedException } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { UserRole } from './users.enum';
import { UsersService } from './users.service';
@Injectable()
export class UserFromTokenPipe implements PipeTransform {
public constructor(
private readonly jsonWebTokenService: JwtService,
private readonly usersService: UsersService
) { }
public async transform(token: string, _metadata: ArgumentMetadata) {
try {
const payload = this.jsonWebTokenService.verify(token);
const user = await this.usersService.findOneById(payload.id);
if (!user) {
throw new UnauthorizedException("Invalid user");
}
return user;
} catch (error) {
if (error instanceof UnauthorizedException) {
throw error;
}
throw new UnauthorizedException("Token");
}
}
}
By creating a parameter decorator and a pipe, you can virtually turn anything from the request into a concrete object, the most obvious is to turn a JSON Web Token into an authenticated user but I'll let you find other use cases as well.