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.
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
| Type | Shape | Typical use |
|---|---|---|
| Unary | One request, one response | Most calls: fetch, create, update |
| Server streaming | One request, a stream of responses | Large result sets, live updates, log tailing |
| Client streaming | A stream of requests, one response | Uploads, metrics ingestion, batched writes |
| Bidirectional streaming | Both sides stream independently | Chat, 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.
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?