Let's have this controller in NestJS project:
@Post('resetpassword')
@HttpCode(200)
async requestPasswordReset(
@Body() body: RequestPasswordResetDTO,
): Promise<boolean> {
try {
return await this.authService.requestPasswordReset(body);
} catch (e) {
if (e instanceof EntityNotFoundError) {
// Throw same exception format as class-validator throwing (ValidationError)
} else throw e;
}
}
Dto definition:
export class RequestPasswordResetDTO {
@IsNotEmpty()
@IsEmail()
public email!: string;
}
I want to throw error in ValidationError
format (property, value, constraints, etc) when this.authService.requestPasswordReset(body);
throws an EntityNotFoundError
exception.
How I can create this error manually? Those errors are just thrown when DTO validation by class-validator
fails. And those can be just static validations, not async database validations.
So the final API response format should be for example:
{
"statusCode": 400,
"error": "Bad Request",
"message": [
{
"target": {
"email": "[email protected]"
},
"value": "[email protected]",
"property": "email",
"children": [],
"constraints": {
"exists": "email address does not exists"
}
}
]
}
I need it to have consistent error handling :)
When adding the ValidationPipe
to your app, provide a custom exceptionFactory
:
app.useGlobalPipes(
new ValidationPipe({
exceptionFactory: (validationErrors: ValidationError[] = []) => {
return new BadRequestException(validationErrors);
},
})
);
This should be all you need to get the intended result.
For comparison, you can check out the original NestJS version here.
You could use an Exception Filter to create your customized response to that exception First we define the Exception Filter:
import { ExceptionFilter, Catch, ArgumentsHost, HttpException } from '@nestjs/common';
import { Request, Response } from 'express';
// import { EntityNotFoundError } from 'wherever';
@Catch(EntityNotFoundError)
export class EntityNotFoundExceptionFilter implements ExceptionFilter {
catch(exception: HttpException, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
const status = exception.getStatus();
response
.status(status)
.json({
"statusCode": 400,
"error": "Bad Request",
"message": [
{
"target": {},
"property": "email",
"children": [],
"constraints": {
"isEmail": "email must be an email"
}
},
// other field exceptions
]
});
}
}
Then back in your controller, you use the filter:
// ...
import { EntityNotFoundExceptionFilter } from 'its module';
// ...
@Post('resetpassword')
@HttpCode(200)
@UseFilters(EntityNotFoundExceptionFilter)
async requestPasswordReset(
@Body() body: RequestPasswordResetDTO
): Promise<boolean> {
return await this.authService.requestPasswordReset(body);
}
This should work just fine.
We can get back the exception response thrown by class-validator
and set to response,
import {
ArgumentsHost,
BadRequestException,
Catch,
ExceptionFilter
} from '@nestjs/common';
@Catch()
export class ValidationFilter < T > implements ExceptionFilter {
catch (exception: T, host: ArgumentsHost) {
if (exception instanceof BadRequestException) {
const response = host.switchToHttp().getResponse();
response.status(exception.getStatus())
.json(exception.getResponse());
}
}
}
Controller should look,
@Post('create')
@UsePipes(ValidationPipe)
@UseFilters(ValidationFilter)
async create(@Body() body: CreateDto) {
}
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