Trial && Error

Intrusive Thoughts & Lost Bits

View on GitHub
16 October 2025

gRPC | When, Why and How

by GonzaloMB

gRPC is a high‑performance RPC framework built on HTTP/2 and Protocol Buffers. This guide covers when to use it, why to choose it over plain HTTP/REST for service‑to‑service traffic, and includes concise Node.js (JavaScript) examples for unary RPC, streaming, deadlines, errors, security, and best practices.


When To Use gRPC

When to prefer HTTP/REST:


Why gRPC


Patterns vs. HTTP/REST

You can expose REST externally while using gRPC internally, or place an HTTP/JSON → gRPC gateway in front.


Quick Setup (Node.js)

  1. Install dependencies:
npm i @grpc/grpc-js @grpc/proto-loader
  1. Define the contract (protos/hello.proto):
syntax = "proto3";
package hello;

service Greeter {
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  rpc StreamGreetings (HelloRequest) returns (stream HelloReply) {}
}

message HelloRequest { string name = 1; }
message HelloReply { string message = 1; }
  1. Server (unary + server streaming):
const path = require("node:path");
const grpc = require("@grpc/grpc-js");
const protoLoader = require("@grpc/proto-loader");

const packageDefinition = protoLoader.loadSync(
  path.join(__dirname, "protos/hello.proto"),
  {
    keepCase: false,
    longs: String,
    enums: String,
    defaults: true,
    oneofs: true,
  }
);
const proto = grpc.loadPackageDefinition(packageDefinition).hello;

function sayHello(call, callback) {
  const name = call.request && call.request.name ? call.request.name : "world";
  callback(null, { message: `Hello, ${name}` });
}

async function streamGreetings(call) {
  const name = call.request && call.request.name ? call.request.name : "world";
  for (const i of [1, 2, 3]) {
    call.write({ message: `Hello ${name} #${i}` });
    await new Promise((r) => setTimeout(r, 300));
  }
  call.end();
}

const server = new grpc.Server();
server.addService(proto.Greeter.service, {
  SayHello: sayHello,
  StreamGreetings: streamGreetings,
});
server.bindAsync("0.0.0.0:50051", grpc.ServerCredentials.createInsecure(), () =>
  server.start()
);
console.log("gRPC server on 50051");
  1. Client (unary + server streaming + deadline):
const path = require("node:path");
const grpc = require("@grpc/grpc-js");
const protoLoader = require("@grpc/proto-loader");

const packageDefinition = protoLoader.loadSync(
  path.join(__dirname, "protos/hello.proto")
);
const proto = grpc.loadPackageDefinition(packageDefinition).hello;
const client = new proto.Greeter(
  "localhost:50051",
  grpc.credentials.createInsecure()
);

// Unary with deadline
const deadline = new Date(Date.now() + 500); // 500ms
client.SayHello({ name: "Ada" }, { deadline }, (err, res) => {
  if (err) return console.error("error:", err.code, err.message);
  console.log("unary:", res.message);
});

// Server streaming
const stream = client.StreamGreetings({ name: "Ada" });
stream.on("data", (m) => console.log("stream:", m.message));
stream.on("end", () => console.log("done"));

RPC Types

Tip: avoid huge messages; prefer chunking or references to external object storage.


Protocol Buffers (Safe Evolution)

Evolution example:

message User {
  string id = 1;
  string email = 2; // reserved 3 (old phone)
  optional string display_name = 4;
}

reserved 3; // legacy field

Errors, Deadlines and Retries

Snippet (Node.js):

const grpc = require("@grpc/grpc-js");

function withDeadline(ms) {
  return { deadline: new Date(Date.now() + ms) };
}

function shouldRetry(code) {
  return (
    code === grpc.status.UNAVAILABLE || code === grpc.status.DEADLINE_EXCEEDED
  );
}

Security

TLS example (client):

const grpc = require("@grpc/grpc-js");
const { readFileSync } = require("node:fs");

const rootCerts = readFileSync("certs/ca.pem");
const creds = grpc.credentials.createSsl(rootCerts);
// const client = new proto.Greeter("greeter.prod:443", creds);

Observability

Client interceptor (minimal logging):

const grpc = require("@grpc/grpc-js");

const loggingInterceptor = (options, nextCall) => {
  const method = options.method_definition.path;
  const start = Date.now();
  const requester = {
    start: (metadata, listener, next) => {
      const newListener = {
        onReceiveStatus: (status, nextStatus) => {
          console.log(method, status.code, Date.now() - start + "ms");
          nextStatus(status);
        },
      };
      next(metadata, newListener);
    },
  };
  return new grpc.InterceptingCall(nextCall(options), requester);
};

Best Practices


Benefits (TL;DR)


Resources


tags: grpc - microservices