Microservices and Custom exception filter in NestJS

Saad Bash

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

      ```typescript
      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.