GrpcClient.js

const grpc = require('grpc');
const protoLoader = require('@grpc/proto-loader');

class GrpcClient {
	/**
	 * @classdesc GrpcClient 
	 * > * You need to create instance of GrpcClient to call remote functions implemented in your services
	 * @param {object} options
	 * @param {string} options.proto Sevice protobuf schema ".proto" file
	 * @param {number} options.service gRPC service name (same name defined in protobuf schema)
	 * @param {number} options.url gRPC server url (host:port)
   * @param {object} [options.loaderOptions] [Protobuf loader](https://github.com/grpc/grpc-node/tree/master/packages/proto-loader) configurations
	 */
	constructor(options){
		const { proto, service, url, loaderOptions } = options;

		this._client = null;
		this._proto = proto;
		this._service = service;
		this._url = url;
		this._loaderOptions = loaderOptions || {
			keepCase: true,
			longs: String,
			enums: String,
			arrays: true,
			defaults: false
		};

		const packageDefinition = protoLoader.loadSync(this._proto, this._loaderOptions);
		const GrpcService = grpc.loadPackageDefinition(packageDefinition)[this._service];
		this._client = new GrpcService(this._url, grpc.credentials.createInsecure());
	}

	/**
	 * Actual remote functions call happen here
	 * @param {function} method method name on gRPC remote service
	 * @param {object} [data] object contains all required arguments for remote method
	 * @param {object} [meta] object contains additional payload data to be passed as gRPC metadata
	 */
	async call(method, data, meta){
		if(!method){
			throw new Error('Remote function name is missing');
		}
		if(!this._client.constructor.service.hasOwnProperty(method)){
			throw new Error('Remote function is missing from proto schema');
		}

		return new Promise((resolve, reject) => {
			let _data = data || {};
			let _meta = {};
			if(meta){
				const md = new grpc.Metadata();
				for(let k in meta){
					let v = meta[k];
					v = (typeof v !== 'string' && v instanceof String) ? String(v) : v;
					md.add(k,v);
				}

				_meta = md;
			}

			this._client[method](_data, _meta, (err, result) => {
				if (err) {
					reject(err);
				}

				resolve(result);
			});
		});
	}
}

module.exports = GrpcClient;