Search code examples
rabbitmqnestjsmicroservicesrpc

Exceptions in microservices on nestjs


I have a microservice application, an api gateway and a service. RabbitMQ is used as a message broker. I realized that in the service you need to use RpcException with message and statusCode instead of HttpException. But I ran into difficulty when using ValidationPipe when validating data in DTO, I get 500 statusCode from api gateway. Because under the hood it throws a BadRequestException. I honestly do not understand what I need to do to convert this exception to the rpc format, or maybe I need to do something else. Perhaps some kind of exceptionFilter is needed, but I do not understand how to do it.

api gateway

@Controller()
export class ApiGateway {
constructor(
    @Inject('AUTH_SERVICE') private authService: ClientProxy,
  ) {}
@Post('auth/register')
  async register(
    @Body('email') email: string,
    @Body('password') password: string | number,
  ) {
    return this.authService.send({ cmd: 'register' }, { email, password });
  }
}

main.ts auth

async function bootstrap() {
  const app = await NestFactory.create(AuthModule);

  const configService = app.get(ConfigService);
  const rmqService = app.get(RmqService);
  const queue = configService.get('RABBITMQ_AUTH_QUEUE');
  app.connectMicroservice(rmqService.getOptions(queue));
  await app.startAllMicroservices();
}
bootstrap();

auth.controller

@Controller()
export class AuthController {
constructor(
    private readonly authService: AuthService,
    private readonly rmqService: RmqService,
  ) {}
@MessagePattern({ cmd: 'register' })
  async register(@Ctx() ctx: RmqContext, @Payload() newUser: UserDto) {
    this.rmqService.acknowledgeMessage(ctx);

    return this.authService.register(newUser);
  }
}

UserDto

@UsePipes(new ValidationPipe({ whitelist: true }))
export class UserDto {
  @IsEmail()
  email: string;

  @IsString()
  @IsNotEmpty()
  password: string;
}

Solution

  • I managed to override HttpException with RpcException. I also redefined the form in which I will receive validation error messages, maybe it will be useful to someone

    export const VALIDATION_PIPE_OPTIONS = {
      transform: true,
      whitelist: true,
      exceptionFactory: (errors) => {
        return new RpcException({
          message: validationErrorsConversion(errors),
          statusCode: 400,
        });
      },
    };
    
    export function validationErrorsConversion(arr) {
      const result = [];
    
      arr.forEach((obj) => {
        const { property, constraints } = obj;
        const values = Object.keys(constraints).map(
          (key) => VALIDATION_ERRORS_CONST[key] || constraints[key],
        );
    
        result.push({
          [property]: values,
        });
      });
    
      return result;
    }
    
    export const VALIDATION_ERRORS_CONST = {
      isEmail: 'Is not email',
      isString: 'It should be a string',
    };