nestjsgrpc-node

NestJS - Grpc client: RpcException from server is serialized to an Error instead of RpcException


I've been at this all day and can't seem to find a solution,
please help :)

The service communication works, the issue is with the error handling...

Scenario:

Service-A is invoked via HTTP. (probably not relevant but just in case)
Service-A calls Service-B via RPC.
Service-B throws an error.
an exception filter on Service-B's controller catches that error and translates it to a RpcException and returns that to to Service-A.
Service-A receives an Error not a RpcExcpetion.

The error is caught in the global error handler as it is not recognized as a RpcException
(would have been caught in Service-B's RpcExceptionFilter...)

Some code:

Service-A:

client configuration
    @Module({
        imports: [...],
        controllers: [...],
        providers: [{
            provide: 'somePackage',
            useFactory(configService: ConfigService) {
                return ClientProxyFactory.create(
                    {
                        transport: Transport.GRPC,
                        options: {
                            url: 'localhost:5000',
                            package: 'somePackage',
                            protoPath: 'pathToProto',
                        },
                    }
                );
            },
            inject: [ConfigService],
        }]
    })
client service injection and init
    export class ServiceA implements OnModuleInit {
    
        private someService: SomeServiceClient;
    
        onModuleInit(): any {
            this.someService = this.client.getService<SomeServiceClient>('SomeService');
        }
    
        constructor(@Inject(somePackage) private client: ClientGrpc)
    }
client method call

await lastValueFrom(this.someService.workWork(workWorkRequest));

Service-B:

error conversion (ExceptionFilter)
catch(exception: RpcException){
        const rpcException: RpcException = someConversionFunction(exception);
        return throwError(() => rpcException.getError());
    }

some logging outputs:

this is what thrown on service-a:
[error][2022-05-30T18:58:13.132Z]-[App/Main] - 9 FAILED_PRECONDITION: Some Service-B error message if i try catch the call and log the error:

Error: 9 FAILED_PRECONDITION: Some Service-B error message
    at Object.callErrorFromStatus (@grpc/grpc-js/src/call.ts:81:24)
    at Object.onReceiveStatus (@grpc/grpc-js/src/client.ts:343:36)
    at Object.onReceiveStatus (@grpc/grpc-js/src/client-interceptors.ts:462:34)
    at Object.onReceiveStatus (@grpc/grpc-js/src/client-interceptors.ts:424:48)
    at @grpc/grpc-js/src/call-stream.ts:323:24
    at processTicksAndRejections (node:internal/process/task_queues:78:11) {
    code: 9,
    details: 'Some Service-B error message',
    metadata: Metadata { internalRepr: Map(0) {}, options: {} }
}

what i expect is basically the same, just as a RpcError:
RpcException [Error]: Some Service-B error message

I've tried to minimize the code as much as possible...
Any input is welcome...
Thanks!


Solution

  • If anyone stumbles across this,
    It is now possible to add an error serializer when creating the ClientProxy like so:

    class ErrorHandlingProxy extends ClientGrpcProxy {
      serializeError(err) {
        return new RpcException(err);
      }
    }
    
    @Module({
      providers: [
        {
          provide: 'HERO_PACKAGE',
          useFactory() {
            return new ErrorHandlingProxy(grpcClientOptions.options);
          },
        },
      ],
      controllers: [HeroController],
    })
    export class HeroModule {}
    

    More details in this issue