Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Promisify @grpc-js service client with typescript
import { Client, ServiceError, Metadata, CallOptions, ClientUnaryCall } from '@grpc/grpc-js';
import { Message } from 'google-protobuf';
type OriginalCall<T, U> = (request: T, metadata: Metadata, options: Partial<CallOptions>, callback: (error: ServiceError, res: U) => void) => ClientUnaryCall;
type PromisifiedCall<T, U> = ((request: T, metadata?: Metadata, options?: Partial<CallOptions>) => Promise<U>);
export type Promisified<C> = { $: C; } & {
[prop in Exclude<keyof C, keyof Client>]: (C[prop] extends OriginalCall<infer T, infer U> ? PromisifiedCall<T, U> : never);
export function promisify<C extends Client>(client: C): Promisified<C> {
return new Proxy(client, {
get: (target, descriptor) => {
let stack = '';
// this step is required to get the correct stack trace
// of course, this has some performance impact, but it's not that big in comparison with grpc calls
try { throw new Error(); } catch (e) { stack = e.stack; }
if (descriptor === '$') {
return target;
return (...args: any[]) => new Promise((resolve, reject) => target[descriptor](...[...args, (err: ServiceError, res: Message) => {
if (err) {
err.stack += stack;
} else {
}) as unknown as Promisified<C>;
export class SearchService {
private searchServiceClient: Promisified<SearchServiceClient>;
constructor(private config: Config) {
const { host, grpcPort } =;
this.searchServiceClient = promisify(new SearchServiceClient(`${host}:${grpcPort}`, ChannelCredentials.createInsecure()));
async search(query: string, limit: number): Promise<SearchResult> {
const request = new SearchRequest().setQuery(query);
const response = await;
return {
items: response.getResultsList().map(item => ({
name: item.getName(),
url: item.getUrl(),
Copy link

I got a Element implicitly has an 'any' type because expression of type 'string | symbol' can't be used to index type 'Client'. No index signature with a parameter of type 'string' was found on type 'Client'. error on line 19, target[descriptor].

I don't know if I had a wrong typescript setting.


Copy link

smnbbrv commented May 25, 2022

You should explicitly use generate_package_definition if you use . Example: grpc_tools_node_protoc --plugin=protoc-gen-ts=../../node_modules/.bin/protoc-gen-ts --ts_out=generate_package_definition:... ...

Copy link

Copy link

This was a big help.

I needed to make a small change to Promisified<C> to make it work on clients that have both unary and streaming calls. I changed the : never at the end to : any.

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