bschulte3
bschulte3

Reputation: 172

Node gRPC with Typescript verbosity

I'm trying to setup a Node gRPC server in Typescript and I've got everything working, but the verbosity of handling the requests/responses leaves me with a bad feeling. I've followed the example of https://github.com/blokur/grpc-ts-demo which seems to be the way a lot of example projects set things up.

The problem comes in when handling a request or response and the code generation tool (grpc_tools_node_protoc) stubs that are created require you to handle requests and responses like this:

  async getAllDogs(
    call: grpc.ServerWritableStream<Empty, DogEntity>,
  ): Promise<void> {
    const dogs = await dogService.repo.findAll();

    dogs
      .map((dogObj): DogEntity => {
        const dogEntity = new DogEntity();
        dogEntity.setBreed(dogObj.breed);
        dogEntity.setId(dogObj.id);
        dogEntity.setGoodBoyOrGirl(dogObj.goodBoyOrGirl);

        return dogEntity;
      })
      .forEach((dog) => {
        call.write(dog);
      });

    call.end();
  }

The type that's generated for DogEntity from grpc_tools_node_protoc is:

export class DogEntity extends jspb.Message { 
    getId(): number;
    setId(value: number): DogEntity;
    getBreed(): string;
    setBreed(value: string): DogEntity;
    getGoodBoyOrGirl(): boolean;
    setGoodBoyOrGirl(value: boolean): DogEntity;

    serializeBinary(): Uint8Array;
    toObject(includeInstance?: boolean): DogEntity.AsObject;
    static toObject(includeInstance: boolean, msg: DogEntity): DogEntity.AsObject;
    static extensions: {[key: number]: jspb.ExtensionFieldInfo<jspb.Message>};
    static extensionsBinary: {[key: number]: jspb.ExtensionFieldBinaryInfo<jspb.Message>};
    static serializeBinaryToWriter(message: DogEntity, writer: jspb.BinaryWriter): void;
    static deserializeBinary(bytes: Uint8Array): DogEntity;
    static deserializeBinaryFromReader(message: DogEntity, reader: jspb.BinaryReader): DogEntity;
}

export namespace DogEntity {
    export type AsObject = {
        id: number,
        breed: string,
        goodBoyOrGirl: boolean,
    }
}

So if the message has a bunch of fields or if you have to write a bunch of RPC handlers this can become pretty verbose to construct the responses (and requests on the client side).

I'm wondering if anyone has implemented this in a way that provides a layer of abstraction to cut out down on the amount of code that's required to just do something like setup some CRUD-type RPC endpoints.

Upvotes: 0

Views: 1005

Answers (1)

bschulte3
bschulte3

Reputation: 172

I actually ended up finding an alternative for stub code generation: https://github.com/stephenh/ts-proto

This library allows for more flexible types, though it requires protoc directly to be run. The main benefit for me in a nutshell is the responses can now be created like this:

  async getAllDogs(
    call: grpc.ServerWritableStream<Empty, DogEntity>,
  ): Promise<void> {
    const dogs = await dogService.repo.findAll();

    dogs.forEach((dog) => {
      call.write(DogEntity.fromJSON(dog));
    });

    call.end();
  }

Upvotes: 1

Related Questions