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);
};
}
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;
}
);
};
}
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"}
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);
}
);
});
};
}
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")
Top comments (0)