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
You are returning exception here instead of throwing it:
Try to replace
return
withthrow
.