Back to the map

Module 04 · API fundamentals · Lesson 9 of 15

gRPC fundamentals

Typed remote procedure calls over HTTP/2 with protobuf: why it is the default between services, the four call types including streaming, deadlines and status codes, and the browser and load-balancing gaps you must plan around.

CoreShould-knowMid 11 min Updated Nov 2026

Why it matters

Once a system has more than a handful of services, the calls between them dominate traffic, and the properties that matter change: compactness, low latency, generated typed clients in several languages, and streaming. gRPC was designed for exactly that path. It also has two sharp edges, browsers and load balancing, that catch teams who adopt it as a general replacement for REST.

The idea

gRPC is a remote procedure call framework. You define services and messages in a .proto file, a compiler generates client and server code in each language, and calls travel as protobuf messages over HTTP/2. The caller invokes orders.GetOrder(request) as if it were a local function; the framework handles serialization, transport, deadlines and errors.

Four call types

TypeShapeTypical use
UnaryOne request, one responseMost calls: fetch, create, update
Server streamingOne request, a stream of responsesLarge result sets, live updates, log tailing
Client streamingA stream of requests, one responseUploads, metrics ingestion, batched writes
Bidirectional streamingBoth sides stream independentlyChat, real-time collaboration, long-lived agent connections

Streaming rides on HTTP/2 multiplexing: many calls share one TCP connection, frames interleave, and head-of-line blocking at the HTTP layer disappears. This is why a gRPC service can hold a few connections per client rather than a pool of hundreds.

Clientgenerated stubOrders servicegenerated serverGetOrder (unary)ListOrders (server stream)UpdateOrder (unary)single HTTP/2 connection, protobuf frames, deadline on every call
One HTTP/2 connection carries several concurrent calls, including a server stream, as interleaved frames.

Deadlines, status codes and metadata

Every gRPC call carries a deadline, an absolute time by which the caller gives up. It propagates: a service receiving a call with 200 ms left passes at most 200 ms to its own downstream calls, so a slow leaf cannot hold the whole chain. Errors use a small fixed set of status codes (NOT_FOUND, INVALID_ARGUMENT, UNAVAILABLE, DEADLINE_EXCEEDED, RESOURCE_EXHAUSTED and a few more) with optional typed error details, so retry policies can be generic: retry UNAVAILABLE, never retry INVALID_ARGUMENT. Metadata (headers) carries auth tokens, trace ids and tenant information.

The two gaps

Browsers cannot make raw gRPC calls because browser HTTP APIs do not expose HTTP/2 framing. Options are gRPC-Web through a proxy (Envoy) that translates, a REST or GraphQL gateway in front of the gRPC services, or Connect-style protocols that work over HTTP/1.1 too. Internally gRPC, externally something browsers speak, is the usual arrangement.

Load balancing is harder because connections are long-lived. A layer-4 balancer distributes connections, not calls, so one busy client pins one server. Solutions are client-side load balancing with service discovery (the client holds a connection to each backend and round-robins calls), a layer-7 proxy that understands HTTP/2 and balances per request, or a service mesh sidecar that does this for every service transparently.

Evolving a gRPC API

Protobuf's rules apply: add fields with new numbers, never reuse numbers, mark removed fields reserved. Adding RPC methods is safe; changing a method's request or response type is a new method. Because clients are generated, incompatible changes fail at build time rather than in production, which is one of the strongest arguments for gRPC internally.

Trade-offs

gRPC buys compact fast calls, typed clients across languages, native streaming and propagated deadlines. It costs browser compatibility, debuggability (binary payloads need tooling like grpcurl), load-balancer sophistication, and a protobuf toolchain in every repo. It fits service-to-service calls at volume and poorly fits public APIs or anything a browser calls directly.

In an interview

Place it precisely: "Service-to-service calls are gRPC with protobuf for typed contracts and low overhead; every call carries a deadline that propagates downstream, and we retry only UNAVAILABLE with backoff. Browsers hit a REST gateway that fans out to gRPC." If asked about scaling, name the connection problem: "Long-lived HTTP/2 connections need per-request balancing, so we use a mesh sidecar rather than a layer-4 balancer."

Check yourself

After adding a second instance of a gRPC service behind a TCP load balancer, almost all traffic still hits the first instance. Why?

gRPC fundamentals · Backplane