Skip to content

Instantly share code, notes, and snippets.

@vitalets
Created September 23, 2021 12:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vitalets/c52c3efa40fc1154ccc6639903758565 to your computer and use it in GitHub Desktop.
Save vitalets/c52c3efa40fc1154ccc6639903758565 to your computer and use it in GitHub Desktop.
Promisify GRPC client and allow plain objects in and out of method calls
/**
* Promisify GRPC client and allow plain objects in and out of method calls.
*/
import { promisify } from 'util';
import * as jspb from 'google-protobuf';
import * as grpc from '@grpc/grpc-js';
type GrpcCallback<Res extends jspb.Message> = (e: grpc.ServiceError | null, res: Res) => void;
type GrpcAsyncMethod<Req extends jspb.Message, Res extends jspb.Message> = {
(req: Req, m: grpc.Metadata, o: Partial<grpc.CallOptions>, cb: GrpcCallback<Res>): void;
};
export type PromisedGrpcClient<T> = {
[K in keyof T]: T[K] extends GrpcAsyncMethod<infer Req, infer Res>
? (req?: Req | Partial<ReturnType<Req['toObject']>>) => Promise<ReturnType<Res['toObject']>>
: never
}
export function promisifyGrpcClient<T extends grpc.Client>(client: T) {
const methods = Object.keys(client.constructor.prototype) as (keyof typeof client)[];
methods.forEach(key => {
const method = client[key];
if (typeof method === 'function') {
const fn = promisify(method).bind(client);
// @ts-expect-error valid
client[key] = async (req?: jspb.Message | Record<string, unknown>) => {
if (!(req instanceof jspb.Message)) req = fromObject(method, req);
const res = await fn(req) as jspb.Message;
return res.toObject();
};
}
});
return client as unknown as PromisedGrpcClient<T>;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function fromObject(method: any, obj?: Record<string, unknown>) {
const req = new method.requestType();
if (obj) {
Object.keys(obj).forEach(key => {
const reqMethodName = `set${capitalizeFirstLetter(key)}`;
req[reqMethodName](obj[key]);
});
}
return req;
}
function capitalizeFirstLetter(s: string) {
return s.charAt(0).toUpperCase() + s.slice(1);
}
@vitalets
Copy link
Author

vitalets commented Sep 23, 2021

Usage example:

import { promisifyGrpcClient } from "./promisify-grpc-client";
import { UserServiceClient } from "../generated/users_pb_service";

const rawClient = new UserServiceClient("https://my.grpc/server");
const client = promisifyGrpcClient(rawClient);
const user = await client.getUser({ username: "johndoe" });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment