Reputation: 172
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
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