Skip to content
Advertisement

NestJs transform GRPC exception to HTTP exception

I have a HTTP server that connects to a gateway over GRPC. the gateway also connects to other . GRPC microservices. the flow looks like this:

Client -> HttpServer -> GRPC server (gateway) -> GRPC microservice server X

The way i handle errors currently is like so (please let me know if there is better practice) i will only show nessaccery code for brevity

GRPC microservice server X

  @GrpcMethod() get(clientDetails: Records.UserDetails.AsObject): Records.RecordResponse.AsObject {
    this.logger.log("Get Record for client");
    throw new RpcException({message: 'some error', code: status.DATA_LOSS})
  }

this simple throws an error to the GRPC client (which works fine)

GRPC Server

  @GrpcMethod() async get(data: Records.UserDetails.AsObject, metaData): Promise<Records.RecordResponse.AsObject> {
    try {
      return await this.hpGrpcRecordsService.get(data).toPromise();
    } catch(e) {
      throw new RpcException(e)
    }
  }

Grpc server catches the error which is in turn caught buy the global exception handler (this works fine)

@Catch(RpcException)
export class ExceptionFilter implements RpcExceptionFilter<RpcException> {
  catch(exception: RpcException, host: ArgumentsHost): Observable<any> {
    if( Object.prototype.hasOwnProperty.call(exception, 'message') && 
        Object.prototype.hasOwnProperty.call(exception.message, 'code') &&
        exception.message.code === 2
    ){ 
        exception.message.code = 13
    }

    return throwError(exception.getError());
  }
}

This throws the error back to the Http server (grpc client, works fine)

Now when it gets to the Http server i was hoping i could set up another RPC exception handler and transform the error into a HTTP except. but i’m unsure if it is possible, i have only been using nest for a few days and am yet to full understand it.

Here is an example of what i was hoping to do (code is not working, just example of what i want). id prefer to globally catch the exceptions rather than have try/catch blocks everywhere

@Catch(RpcException)
export class ExceptionFilter implements RpcExceptionFilter<RpcException> {
  catch(exception: RpcException, host: ArgumentsHost): Observable<any> {
    //Map UNKNOWN(2) grpc error to INTERNAL(13)
    if( Object.prototype.hasOwnProperty.call(exception, 'message') && 
        Object.prototype.hasOwnProperty.call(exception.message, 'code') &&
        exception.message.code === 2
    ){  exception.message.code = 13 }

    throw new HttpException('GOT EM', HttpStatus.BAD_GATEWAY)
  }
}

Advertisement

Answer

I have been stuck at the same place for some time now. What seems to work is that only the string you send as message gets received at the HTTP server. So the code below as a filter in HTTP server works, but you have to check for status via the message string.

@Catch(RpcException)
export class HttpExceptionFilter implements ExceptionFilter {
  catch(exception: RpcException, host: ArgumentsHost) {

    const err = exception.getError();
    // console.log(err);
    const ctx = host.switchToHttp();
    const response = ctx.getResponse<Response>();
    const request = ctx.getRequest<Request>();
    response
      .json({
        message: err["details"],
        code: err['code'],
        timestamp: new Date().toISOString(),
        path: request.url,
      });
  }
}
 if(err['details'] === UserBusinessErrors.InvalidCredentials.message){
 this.logger.error(e);
     throw new HttpException( UserBusinessErrors.InvalidCredentials.message, 409)
 } else {
     this.logger.error(e);
     throw new InternalServerErrorException();
 }
User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement