'Throw same error format as `class-validator` in NestJS
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 :)
Solution 1:[1]
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.
Solution 2:[2]
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.
Solution 3:[3]
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) {
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Joshua |
| Solution 2 | toondaey |
| Solution 3 | HenonoaH |
