In any API, error handling is an essential aspect of ensuring smooth communication between the backend and frontend. In NestJS, error handling is made simple and scalable with the use of Exception Filters. These filters catch and handle exceptions thrown by your application, allowing you to format and customize error responses globally or at the controller level.
What is an Exception Filter in NestJS?
An Exception Filter in NestJS allows you to catch and process errors thrown during the execution of requests. It provides a centralized way to manage and respond to errors consistently across your application. Exception filters can be applied globally or at the controller level, giving you fine-grained control over error handling.
Key Features of Exception Filters:
- Intercept errors thrown during request handling.
- Format error responses uniformly.
- Customize error responses, including adding detailed messages or error codes.
- Apply globally or to specific controllers.
Creating the GlobalExceptionFilter
Let’s dive into the implementation of a GlobalExceptionFilter in NestJS. This filter will catch all errors, format them in a consistent structure, and send them back to the client.
import {
ExceptionFilter,
Catch,
ArgumentsHost,
HttpException,
HttpStatus,
} from '@nestjs/common';
import {
PrismaClientInitializationError,
PrismaClientKnownRequestError,
PrismaClientRustPanicError,
PrismaClientUnknownRequestError,
PrismaClientValidationError,
} from '@prisma/client/runtime/library';
import { Response } from 'express';
import { DtoValidationFormatter } from 'src/ExceptionsFormatter/dto.exceptions';
import { OtherExceptionFormatter } from 'src/ExceptionsFormatter/other.exceptions';
import { PrismaExceptionFormatter } from 'src/ExceptionsFormatter/prisma.exceptions';
export interface IErrorMessage {
path: string;
message: string;
}
@Catch()
export class GlobalExceptionFilter implements ExceptionFilter {
constructor(
private readonly prismaExceptionFormatter: PrismaExceptionFormatter,
private readonly dtoValidationFormatter: DtoValidationFormatter,
private readonly otherValidationFormatter: OtherExceptionFormatter,
) {}
catch(exception: any, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const status =
exception instanceof HttpException
? exception.getStatus()
: HttpStatus.INTERNAL_SERVER_ERROR;
let errorMessages: IErrorMessage[] = [
{ path: 'unknown', message: 'Internal server error' },
];
if (
exception instanceof PrismaClientKnownRequestError ||
exception instanceof PrismaClientValidationError ||
exception instanceof PrismaClientRustPanicError ||
exception instanceof PrismaClientUnknownRequestError ||
exception instanceof PrismaClientInitializationError
) {
console.log('prisma');
errorMessages = this.prismaExceptionFormatter.formatError(exception);
} else if (exception instanceof HttpException) {
console.log('dto');
errorMessages =
this.dtoValidationFormatter.formatDtoValidationException(exception);
} else {
console.log('other');
errorMessages = this.otherValidationFormatter.formatOtherError(exception);
}
// Send the response with a proper error structure
response.status(status).json({
success: false,
message: 'Validation error',
errorMessages,
});
}
}
Applying the GlobalExceptionFilter
To ensure the GlobalExceptionFilter is used across the entire application, we need to register it in the app.module.ts file of the NestJS application:
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { APP_FILTER } from '@nestjs/core';
import { GlobalExceptionFilter } from './interceptors/exception.interceptor';
import { PrismaExceptionFormatter } from './ExceptionsFormatter/prisma.exceptions';
import { DtoValidationFormatter } from './ExceptionsFormatter/dto.exceptions';
import { OtherExceptionFormatter } from './ExceptionsFormatter/other.exceptions';
@Module({
imports: [],
controllers: [AppController],
providers: [
AppService,
PrismaExceptionFormatter,
DtoValidationFormatter,
OtherExceptionFormatter,
{
provide: APP_FILTER,
useClass: GlobalExceptionFilter,
},
],
})
export class AppModule {}
Benefits of Using a GlobalExceptionFilter
Consistency in Error Handling:
By using a global exception filter, we ensure that all errors are handled and structured in the same way, making it easier for frontend developers to parse and display errors.Centralized Error Management:
The filter helps keep error-handling logic centralized, making it easier to manage and modify. Changes to error formats or behavior only need to be made in one place.Customizable Responses:
You can further customize the GlobalExceptionFilter to add more information, such as error codes, user-friendly messages, or even logging for debugging.Improved Developer Experience:
Developers working on the API can focus on business logic while relying on the global exception filter to handle any unexpected errors in a standardized way.
Read this article to format prisma error.
Regards,
N I Rimon