gRPC is Google's open-source RPC framework over HTTP/2, with Protocol Buffers as both schema and wire format. You write a .proto file, generate clients and servers in any language, and call remote methods like local functions — typed, fast, and tiny on the wire.
.proto file defines messages and service methods. The schema is the API.protoc generates clients and servers in 10+ languages — Go, Java, C++, Python, Node, C#, Rust, Swift, Kotlin, Dart.Protobuf encodes an integer in 1–10 bytes, a typical message in a fraction of the JSON size. No field names on the wire. Combined with HTTP/2 multiplexing and header compression, gRPC routinely beats REST/JSON by 5–10× on throughput at the same hardware.
You don't write the API and document it; you write the schema and generate the API. The Java service, the Go client, and the Node test harness all derive from the same .proto. Drift between client and server expectations becomes a compile error, not a 3 a.m. page.
HTTP/2 streams give gRPC first-class streaming in any direction without a separate protocol. Server-streaming for log tails, client-streaming for batched uploads, bidirectional for chat-style exchanges. No WebSocket framing to invent.
A client sets a deadline; servers receive it; if they call downstream services, the deadline rides along. Cancellation propagates the same way. Compared to REST — where every service has to set its own timeout — this is a major operational gift.
Browsers can't speak gRPC directly — the JS fetch API can't expose the HTTP/2 frames gRPC needs. You either run gRPC-Web (a subset, with a proxy like Envoy translating to native gRPC behind the scenes) or you put a REST/GraphQL gateway in front. This is why most public APIs aren't gRPC even when the backend is.
You can't curl a gRPC call meaningfully — the binary payload is unreadable without the schema. Tools like grpcurl, evans, BloomRPC fill the gap, but the casual debuggability of REST is gone.
Every hop — load balancers, proxies, CDNs — has to support HTTP/2 trailers and gRPC framing. Older AWS ALBs, classic CloudFront setups, and corporate proxies often don't. Modern infra (Envoy, NGINX 1.13.10+, Kubernetes ingress with proper config) handles it; older infra fights you.
Field numbers are sacred — once assigned, never reuse them. Renaming a field is fine on the wire (only numbers matter); deleting one and reusing the number breaks every old client. Use a tool like buf in CI to enforce backward compatibility.
Clients and servers depend on generated code from the schema. You need a discipline for distributing the schema (a shared .proto repo, a Buf Schema Registry) and rebuilding when it changes. More involved than "the curl command still works."
Pick gRPC for service-to-service inside your platform when you want type safety, streaming, and throughput. Keep REST or GraphQL at the edge for browsers and third parties — usually as an API gateway sitting in front of a gRPC backbone.
Don't pick gRPC for public APIs (gRPC-Web is awkward, schema distribution to outsiders is ops-heavy), file-heavy workloads (large messages strain HTTP/2 streams), or organizations without the appetite for codegen and schema governance.