DEV Community

Domantas Jurkus
Domantas Jurkus

Posted on

Node.js gRPC calls and Promises

I was connecting a gRPC service with my node service and ran into an issue that i wanted to document for future sake.

The problem

I have a controller that looks like this:

export class MyController {
  static getItem = async (_req: Request, res: Response): Promise<void> => {
    const data = await myService.getItem();

    console.log('controller: ', data);

    res.status(200).json(data);
  };
}
Enter fullscreen mode Exit fullscreen mode

My service looks like this:

export class IstService {
  // ... setup stuff

  getItem = async () => {
    await this.grpcClient.GetItem(
      { item_id: 'item-1234' },
      (err: any, response: Record<string, unknown>) => {
        if (err) {
          console.error(err);
          return;
        }
        console.log('service:', JSON.stringify(response));

        return response;
      }
    );
  };
}
Enter fullscreen mode Exit fullscreen mode

When I call the controller, the log order is:

apps/server start:dev: [1] controller: undefined
apps/server start:dev: [1] service: {"id": "item-1234", "foo": "bar"}
Enter fullscreen mode Exit fullscreen mode

Uh oh, that's not what I want. While I have an await before myService.getItem(), the controller still finishes execution first before the gRPC call finishes.

The solution

The solution to this problem is to wrap the service gRPC call into a new Promise:

export class IstService {
  // ... setup stuff

  getItem = async () => {
    return new Promise((resolve, reject) => {
      this.grpcClient.GetItem(
        { item_id: 'item-1234' },
        (err: any, response: Record<string, unknown>) => {
          if (err) {
            reject(err);
            return;
          }
          console.log('service:', JSON.stringify(response));

          resolve(response);
        }
      );
    });
  };
}
Enter fullscreen mode Exit fullscreen mode

The main issue here is that this.grpcClient.GetItem does not use the async/await API - it uses the traditional callback pattern, where you essentially have to nest your code into the callback function.

JavaScript/TypeScript does not stop you from putting await on places where it does not make sense (would be nice is TypeScript had some automatic warning for pointless cases like these):

await console.log("just weird")
Enter fullscreen mode Exit fullscreen mode

Top comments (0)