gRPC for JavaScript (Node.js): libraries and examples
gRPC is a high-performance remote procedure call (RPC) framework developed by Google. It uses Protocol Buffers (protobuf) for data serialization and HTTP/2 for transport. In the Node.js ecosystem, gRPC allows you to create reliable microservices with strong typing, streaming support, and bidirectional communication. In this article, we will review the main libraries for working with gRPC in JavaScript (Node.js) and walk through a practical server and client example.
Main libraries for gRPC in Node.js
Two main packages are used for working with gRPC in Node.js:
- @grpc/grpc-js — a pure JavaScript implementation of gRPC. It requires no native dependencies, works on all platforms, and supports all gRPC features (unary calls, server and client streaming, bidirectional streaming).
- @grpc/proto-loader — a utility for loading
.protofiles at runtime. It allows dynamically creating JavaScript objects from Protobuf definitions.
For compiling .proto files into static modules, you can use grpc-tools (part of grpc-tools), but in modern projects dynamic loading via @grpc/proto-loader is more commonly used.
Installing dependencies
npm install @grpc/grpc-js @grpc/proto-loaderExample: defining a Proto file
Let's create a helloworld.proto file with a simple service:
syntax = "proto3";
package helloworld;
service Greeter { rpc SayHello (HelloRequest) returns (HelloReply) {}}
message HelloRequest { string name = 1;}
message HelloReply { string message = 1;}Server side (Node.js)
Let's create a server.js file:
const grpc = require('@grpc/grpc-js');const protoLoader = require('@grpc/proto-loader');const path = require('path');
const PROTO_PATH = path.join(__dirname, 'helloworld.proto');
const packageDefinition = protoLoader.loadSync(PROTO_PATH, { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true});
const helloProto = grpc.loadPackageDefinition(packageDefinition).helloworld;
function sayHello(call, callback) { const name = call.request.name || 'Guest'; callback(null, { message: `Hello, ${name}!` });}
function main() { const server = new grpc.Server(); server.addService(helloProto.Greeter.service, { sayHello: sayHello }); server.bindAsync('0.0.0.0:50051', grpc.ServerCredentials.createInsecure(), () => { console.log('gRPC server started on port 50051'); server.start(); });}
main();Client side (Node.js)
Let's create a client.js file:
const grpc = require('@grpc/grpc-js');const protoLoader = require('@grpc/proto-loader');const path = require('path');
const PROTO_PATH = path.join(__dirname, 'helloworld.proto');
const packageDefinition = protoLoader.loadSync(PROTO_PATH, { keepCase: true, longs: String, enums: String, defaults: true, oneofs: true});
const helloProto = grpc.loadPackageDefinition(packageDefinition).helloworld;
function main() { const client = new helloProto.Greeter('localhost:50051', grpc.credentials.createInsecure());
const userName = process.argv[2] || 'World';
client.sayHello({ name: userName }, (error, response) => { if (error) { console.error('Error:', error.message); return; } console.log('Server response:', response.message); });}
main();Running and testing
Start the server in one terminal:
node server.jsIn another terminal, run the client:
node client.js IvanYou should
Also in library
CryptoKit for Swift: Complete Guide with Examples
JSON Processing in Rust: serde_json and json Libraries
Working with S3 and Cloud Storage in Go: minio-go Library
OpenTelemetry Go: Complete Guide with Code Examples
Working with Images in Rust: Overview of image, fast_image_resize, and other Libraries