Microservices and Custom exception filter in NestJS

Create custom exception filters in NestJS to handle RpcException errors thrown by microservices with better control and customization.

  • Nest offers a default exceptions layer to handle unhandled exceptions in an application. Global microservice exception filters aren't enabled by default when using a hybrid application.
  • In the case of an error occurring on a microservice, it is recommended to use RpcException instead of HttpException.
  • To catch RpcException on the client side, implementing "Exception filters" is a good practice. These filters offer greater control over the flow of the application and allow customization of the response returned to the client from the microservice.

To create a custom exception filter, extend the RpcException:

import { ArgumentsHost, Catch, ExceptionFilter } from "@nestjs/common";
import { RpcException } from "@nestjs/microservices";

@Catch(RpcException)
export class IIRpcExceptionFilter implements ExceptionFilter {
  catch(exception: RpcException, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const error: any = exception.getError();
    const response = ctx.getResponse();
    const message = exception.message;

    // Customize the error response as desired
    response.status(error.status).json({
      status: error.status,
      message: message,
      error: "CustomException",
    });
  }
}

Use the @UseFilters() decorator to apply the filter to the controller or method you want to handle the RpcException error:

import { Controller, Get, UseFilters } from "@nestjs/common";
import { IRpcExceptionFilter } from "./rpc-exception.filter";
import { AppService } from "./app.service";

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}

  @Get()
  @UseFilters(IRpcExceptionFilter)
  async getHello(): Promise<string> {
    return this.appService.getHello();
  }
}
  • To use the RpcException filter globally: main.ts

    import { NestFactory } from '@nestjs/core'
    import { AppModule } from './app.module'
    import { IRpcExceptionFilter } from './rpc-exception.filter'
    
    async function bootstrap() {
        const app = await NestFactory.create(AppModule)
    
        // Add Global Filter in main.ts
        app.useGlobalFilters(new IRpcExceptionFilter())
    
        await app.listen(3000)
    }
    bootstrap()
    

In the microservice client, throw an RpcException with the appropriate status code and message when an error occurs:

import { Controller } from "@nestjs/common";
import { MessagePattern, RpcException } from "@nestjs/microservices";

@Controller()
export class AppController {
  @MessagePattern({ cmd: "getHello" })
  async getHello(): Promise<string> {
    try {
      // ... some code that might throw an error
    } catch (error) {
      throw new RpcException({
        message: error,
        status: HttpStatus.BAD_REQUEST,
      });
    }
  }
}

With these steps, we have created a custom exception filter that handles RpcException errors thrown by a microservice in NestJS.