NestJS TransformResponse Interceptor - nestjs

I Have created Interceptor and Decorator for TransformingResponses in NestJS.
Interceptors looks like this:
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '#nestjs/common';
import { Reflector } from '#nestjs/core';
import { map, Observable } from 'rxjs';
#Injectable()
export class TransformInterceptor implements NestInterceptor {
constructor(private reflector: Reflector) {}
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
const metadata = this.reflector.get<string[]>('TRANSFORM_RESPONSE', context.getHandler());
if (metadata) {
return next.handle().pipe(map((data) => ({ result: data })));
}
return next.handle();
}
}
Issue is that when the service returns null ( entity do not exists in the db or whatever ) this transform response is trying to map the response from the service to some particular DTO. And then in this DTO we are destructing or what ever but when is null you know what is happening...
My question is how to catch this null which is coming from the service in the interceptor before passing it to the DTO and prevent going that route if there is no response from the service

Related

User and its data is undefined on the context.switchToHttp().getRequest() with nestjs guards

I'm new to nestJs and I needed to add role based access to the application so I followed the documentation but in the execution context user doesn't exist. I can't seems to find the problem
here is the full repo https://github.com/callme-MJ/test-sample
roles.guard.ts
import { CanActivate, ExecutionContext, Injectable } from "#nestjs/common";
import { Reflector } from "#nestjs/core";
import { use } from "passport";
import { Role } from "src/typeorm/roles.enum";
#Injectable()
export class RolesGuard implements CanActivate{
constructor (private reflector:Reflector){}
canActivate(context: ExecutionContext): boolean {
const requiredRole = this.reflector.getAllAndOverride<Role[]>('roles',[
context.getHandler(),
context.getClass(),
]);
if (!requiredRole) {
return true
}
const {user} = context.switchToHttp().getRequest();
return requiredRole.some((role)=> user.role.includes(role));
}
}
auth.controller.ts
import { Controller, Get, Post, Request, UseGuards } from '#nestjs/common';
import { AuthService } from 'src/auth/services/auth/auth.service';
import { Role } from 'src/typeorm/roles.enum';
import { JwtAuthGuard } from 'src/utils/guards/jwt.auth.guard';
import { LocalAuthGuard } from 'src/utils/guards/local-auth.guards';
import { RolesGuard } from 'src/utils/guards/roles.guards';
import { Roles } from 'src/utils/roles.decorator';
#Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) { }
#UseGuards(JwtAuthGuard,RolesGuard)
#Get('dashboard')
#Roles(Role.CORDINATOR)
getResponse(#Request() req): any {
return req.user;
}
}
At this line in your repo you have APP_GUARD which is a global guard binding. This means that this RolesGuard will be registered and used before your JwtAuthGuard in the getResponse method of the AuthController. Take a look at the section on "Enabling Authentication Globally" for a way around this. Otherwise, you can remove the APP_GUARD for the RolesGuard and just bind it where necessar

How to get name of module, which controller processing request?

I want to get name of module, which controller processing request.
#Get('/')
getIndex() {
console.log('name of module');
}
I don't know exactly the purpose behind your question, but I'll leave you some alternatives.
First one and the dirtier. You can get the instance of the module that your productController is imported by finding it into the modules container.
import { Controller, Get, Query } from '#nestjs/common';
import { ModulesContainer } from '#nestjs/core';
import { Module } from '#nestjs/core/injector/module';
#Controller('path')
export class ProductController {
constructor(
private modulesContainer: ModulesContainer,
private productService: ProductService
) { }
#Get()
findAll(#Query() dto: any) {
let yourModule: Module;
this.modulesContainer.forEach((v) => {
if(v.controllers.has(this.constructor.name)) { // in this condition, you will find your module based in the imports from it, if your controller is importe in some module it will get the module and put in "yourModule" variable.
// Here
yourModule= v;
}
});
console.log(yourModule);
return this.productService.findAll();
}
}
And for a cleaner approach you can get the moduleRef in your controller
import { Controller, Get, Query } from '#nestjs/common';
import { ModuleRef} from '#nestjs/core';
#Controller('path')
export class ProductController {
constructor(
private moduleRef: ModuleRef,
private productService: ProductService
) { }
#Get()
findAll(#Query() dto: any) {
console.log(this.moduleRef) //your module ref
return this.productService.findAll();
}
}
But of course depends on what's you're trying to do.

how to modify Request and Response coming from PUT using interceptor in NestJs

I am using NestJs. I am using intercepter in my controller for PUT request.
I want to change the request body before the PUT request and I want to change response body that returns by PUT request. How to achieve that?
Using in PUT
#UseInterceptors(UpdateFlowInterceptor)
#Put('flows')
public updateFlow(#Body() flow: Flow): Observable<Flow> {
return this.apiFactory.getApiService().updateFlow(flow).pipe(catchError(error =>
of(new HttpException(error.message, 404))));
}
Interceptor
#Injectable()
export class UpdateFlowInterceptor implements NestInterceptor {
public intercept(_context: ExecutionContext, next: CallHandler): Observable<FlowUI> {
// how to change request also
return next.handle().pipe(
map(flow => {
flow.name = 'changeing response body';
return flow;
}),
);
}
}
I was able to do it by getting request from ExecutionContext
following is the code.
#Injectable()
export class UpdateFlowInterceptor implements NestInterceptor {
public intercept(
_context: ExecutionContext,
next: CallHandler
): Observable<FlowUI> {
// changing request
let request = _context.switchToHttp().getRequest();
if (request.body.name) {
request.body.name = 'modify request';
}
return next.handle().pipe(
map((flow) => {
flow.name = 'changeing response body';
return flow;
})
);
}
}

NestJS How to add custom Logger to custom ExceptionFilter

I am using NestJS 5.4.0
I have custom LoggerService, it's working perfectly. But, how can I add this LoggerService to ExceptionFilter.
// logger.service.ts
import {Injectable, LoggerService} from '#nestjs/common';
#Injectable()
export class Logger implements LoggerService {
log(message: string) {
console.log(message);
}
error(message: string, trace: string) {
console.error(message);
}
warn(message: string) {
console.warn(message);
}
}
//logger.module.ts
import { Module } from '#nestjs/common';
import {Logger} from '../services/logger.service';
#Module({
providers: [Logger],
exports: [Logger],
})
export class LoggerModule {}
// user.module.ts
import { Module } from '#nestjs/common';
import {UserService} from '../services/user.service';
import {LoggerModule} from './logger.module';
#Module({
imports: [LoggerModule],
providers: [UserService],
exports: [UserService],
})
export class UserModule {}
It's working perfectly.
import {Logger} from './logger.service';
export class UserService {
constructor(
private logger: Logger
) {}
private test = () => {
this.logger.log("test"); // log success "test" to console
}
}
But how can I add my custom Logger to ExceptionFilter
// forbidden.exception.filter.ts
import {HttpException, HttpStatus, Injectable} from '#nestjs/common';
#Injectable()
export class ForbiddenException extends HttpException {
constructor(message?: string) {
super(message || 'Forbidden', HttpStatus.FORBIDDEN);
// I want to add my custom logger here!
}
}
Thank for reading.
First of all your class ForbiddenException extends HttpException is not
what it calls ExceptionFilter. ExceptionFilter is
exceptions layer which is responsible for processing all unhandled exceptions across an application
docs
You provided exmaple when you are trying to inject it to your custom HttpException. But thats wrong. Your exception don't have to be responsible for logging. Thats what ExceptionFilter should be responsible for.
Anyway, for now (17 oct 2019) there is no example in official docs how to inject providers to ExceptionFilter.
You can pass it to constructor on init, but you should to get Logger instance before with app.get<T>(...) method.
For example I've changed code from exception-filters docs:
// HttpExceptionFilter.ts
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '#nestjs/common';
import { Request, Response } from 'express';
import {MyLogger} from '../MyLogger'
#Catch(HttpException)
export class HttpExceptionFilter implements ExceptionFilter {
constructor(private readonly logger: MyLogger) {}
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
if (status >= 500) {
this.logger.error({ request, response });
}
response
.status(status)
.json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
});
}
}
and bootstrap.ts code:
// bootstrap.ts
const app = await NestFactory.create(MainModule, {
logger: false,
});
const logger = app.get<MyLogger>(MyLogger);
app.useLogger(logger);
app.useGlobalFilters(new HttpExceptionFilter(logger));
This technique can be used for all this INestApplication methods:
app.useGlobalFilters
app.useGlobalGuards
app.useGlobalInterceptors
app.useGlobalPipes
app.useLogger
app.useWebSocketAdapter
First of all, to use dependency injection with Exception filters you cannot register them using the useGlobalFilters() method:
const app = await NestFactory.create(MainModule, {
logger: false,
});
const logger = app.get<MyLogger>(MyLogger);
app.useLogger(logger);
//Remove this line
//app.useGlobalFilters(new HttpExceptionFilter(logger));
Next in your MainModule, add your custom exception filter as a provider (note: filters are automatically set as global no matter what module you add them to but as a best practice, add them to your top level module):
import { Module } from '#nestjs/common';
import { APP_FILTER } from '#nestjs/core';
import { LoggerModule } from './logger.module';
import { ForbiddenException } from './forbidden.exception.filter.ts';
#Module({
imports: [
LoggerModule //this is your logger module
],
providers: [
{
provide: APP_FILTER, //you have to use this custom provider
useClass: ForbiddenException //this is your custom exception filter
}
]
})
export class MainModule {}
Now you can inject the logger into your custom exception filter:
import {HttpException, HttpStatus, Injectable} from '#nestjs/common';
import { Logger } from './path/to/logger';
#Injectable()
export class ForbiddenException extends HttpException {
constructor(private logger: Logger) {}
catch(exception: HttpException, response) {
this.logger.log('test');
}
}
Pseudo code but I think you get the idea.

Nest.js: Global AuthGuard but with exceptions

I would like to register my AuthenticationGuard, which checks for Authentication, globally on my application, so that by default all routes require authentication.
const authGuard = app
.select(AuthModule)
.get(AuthGuard);
app.useGlobalGuards(authGuard);
What is the best/nest.js way to add route exceptions, so that anonymous routes can also be implemented?
You can actually set metadata for the global AuthGuard so it can determine if it should allow an unauthorized request.
e.g.
Set Global Auth Guard
import { Module } from '#nestjs/common';
import { APP_GUARD } from '#nestjs/core';
import { AuthGuard } from './auth.guard';
#Module({
providers: [
{
provide: APP_GUARD,
useClass: AuthGuard,
},
],
})
export class AppModule {}
Use SetMetadata to pass in data to the AuthGuard
import { SetMetadata } from '#nestjs/common';
// Convienience Function
const AllowUnauthorizedRequest = () => SetMetadata('allowUnauthorizedRequest', true);
#Controller()
export class AppController {
#Get('my-unauthorized-path')
#AllowUnauthorizedRequest()
myHandler () {
return { unauthorized: true };
}
}
Use data passed in from SetMetadata to determine if unauthorized request is allowed.
import { CanActivate, ExecutionContext, Injectable } from '#nestjs/common';
import { Reflector } from '#nestjs/core';
import { validateRequest } from './validateRequest' // your custom implementation
#Injectable()
export class AuthGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext) {
const request = context.switchToHttp().getRequest();
const allowUnauthorizedRequest = this.reflector.get<boolean>('allowUnauthorizedRequest', context.getHandler());
return allowUnauthorizedRequest || validateRequest(request);
}
}
There is no way to configure such behavior in a succinct way. If useGlobalGuards used, the only way to do this is to customize or extend AuthGuard.
See original issue

Resources