NestJs Microservice RPC Exception Handling

1k views Asked by At

I have a question about proper exception handling in NestJS across microservices. Current setup looks like this. Client Request ==(REST)==> API Gateway ==(RabbitMQ / RPC)==> Service-A

In Service-A an exception occurs which I throw as an RPC exception and want to convert to an HTTPException in the API gateway.

The structure of my API gateway looks like this => Controller => Service => RabbitMQ RPC Request

My service A looks like this: => Controller => Service => CQRS => CommandHandler => GenericRepo etc.

I have since figured out that it is best to handle the exception in the controller. Accordingly, my API controller and API service looks like this.

@ApiTags('User')
@Controller('user')
export class UserController {
  private readonly _logger = new Logger(UserController.name);

  constructor(
    @Inject(IRESETSERVICE) private readonly _userService: UserService
    ) {}

  @ApiOperation({ summary: 'Create a new user' })
  @ApiCreatedResponse({ description: 'The user has been successfully created.' })
  @ApiUnprocessableEntityResponse({ description: 'The user could not be created.' })
  @ApiForbiddenResponse({ description: 'Forbidden.' })
  @ApiUnauthorizedResponse({ description: 'Unauthorized.' })
  @ApiInternalServerErrorResponse({ description: 'Internal server error.' })
  @Post()
  async create(@Body() createUserDto: CreateUserDto) {
    try {
      return  this._userService.create(createUserDto);
    }
    catch (error) {
        this._logger.error(error.message);
        return error;
    }
  }
}
@Injectable()
export class UserService implements IRestService {
  private readonly _logger = new Logger(UserService.name)

  constructor(
    private readonly _amqpService: AmqpService    
  ) {};

  async create(createUserDto: CreateUserDto): Promise<CreateUserResponseDto> {
    return this._amqpService.request(AmqpExchange.ACCOUNT_SERVICE, AmqpRoutingKey.USER + AmqpRoutingKeyOperation.CREATE, { dto: createUserDto });
  }
}

The controller in the service incl. service itself look like this:

@Controller()
export class UserController {
  private readonly _logger = new Logger(UserService.name)
  
  constructor(
    @Inject(IAMQPSERVICE) private readonly _usersService: UserService
    ) {}

  @RabbitRPC({
    exchange: AmqpExchange.ACCOUNT_SERVICE,
    routingKey: AmqpRoutingKey.USER + AmqpRoutingKeyOperation.CREATE,
    queue: AmqpRoutingKey.USER + AmqpRoutingKeyOperation.CREATE,
  })
  async create(@RabbitPayload() payload) {
    return this._usersService.create(payload.request.dto);
  }
}
@Injectable()
export class UserService implements IAmqpService {
  private readonly _logger = new Logger(UserService.name)

  constructor(
    private readonly _queryBus: QueryBus,
    private readonly _commandBus: CommandBus,
  ) {}

  async create(createUserDto: CreateUserDto) {
    return this._commandBus.execute(new CreateUserCommand(createUserDto));
  }
}
@CommandHandler(CreateUserCommand)
export class CreateUserHandler implements ICommandHandler<CreateUserCommand> {
  
  constructor(
    private _userUseCases: UserUseCases,
    private _userFactoryService: UserFactoryService
  ) {}

  async execute(command: CreateUserCommand) {
    console.log('Async CreateUserCommand...');

    const createUserResponse = new CreateUserResponseDto();
    try {
      const user = this._userFactoryService.createNewUser(command.createUserDto);
      const createdUser = await this._userUseCases.createUser(user);

      createUserResponse.success = true;
      createUserResponse.createdUser = createdUser;
    } catch (error) {
      // report and log error
      createUserResponse.success = false;
      return new MyRpcException(error,500);
    }

    return createUserResponse;

  }
}

`

The exception thrown in the CommandHandler (MyRpcException) I get back ready as response, but Postman shows me status 201 (Created) despite 500. My MyRpcException Solution is from: How to handle RpcException in NestJS but did not work (Last Comment)

I tested a Custom ExceptionFilter

1

There are 1 answers

1
Tan Nguyen On

You are returning exception here instead of throwing it:

return new MyRpcException(error,500);

Try to replace return with throw.