gRPC Introduction
What gRPC is and why it fits internal service-to-service APIs, and when REST is still the better choice.
What is gRPC?
gRPC is an open-source RPC (Remote Procedure Call) framework, originally built by Google, for calling methods on a remote service as if they were local function calls. Instead of hand-rolling HTTP routes and JSON payloads, you define a service's methods and message shapes once in a .proto file (Protocol Buffers, or "protobuf"), and gRPC's tooling generates client and server code in whatever language you need — Go, C#, Java, Python, and more — all from that single source of truth.
service Greeter {
rpc SayHello (HelloRequest) returns (HelloReply);
}
message HelloRequest {
string name = 1;
}
message HelloReply {
string message = 1;
}
Calling it from generated client code looks like an ordinary local method call:
resp, err := client.SayHello(ctx, &pb.HelloRequest{Name: "Ada"})
fmt.Println(resp.Message)
No manual URL building, no manual JSON marshaling, no hand-written client SDK — all of that is generated directly from the .proto contract.
Why gRPC for internal service-to-service APIs
- Binary encoding, not JSON text — protobuf messages are serialized to a compact binary format. Smaller payloads and faster serialization/deserialization than parsing JSON text, which adds up significantly at high request volumes between internal services.
- A strongly-typed contract — the
.protofile is the API contract, checked into source control and shared by every language's generated code. A field's type and number are fixed and versioned explicitly (see the next page), catching a whole class of "the client and server disagree about the shape of this payload" bugs at compile time instead of at runtime. - HTTP/2 as the transport — gRPC runs on HTTP/2, which supports multiplexing many requests over a single TCP connection (no more head-of-line blocking from opening a new connection per request) and native support for streaming in both directions (covered on the streaming page).
- Code generation for every language — a single
.protofile generates idiomatic client and server code for each supported language, so a Go service and a Java service can call each other with a fully typed API, without either team hand-writing an SDK for the other.
When gRPC is a strong fit
- Internal microservice-to-microservice calls — where both ends are services you control, can deploy in step, and want maximum performance and strict contracts between.
- Polyglot systems — a Go service calling a Python service calling a Java service, all sharing one generated contract instead of three hand-maintained client libraries.
- Streaming workloads — live data feeds, chat, or any interaction that isn't a clean single-request/single-response shape (the next two pages cover this in depth).
When REST/JSON is still the better choice
- Public APIs — gRPC's binary format and HTTP/2 requirement make it awkward to call directly from a browser (though gRPC-Web exists as a workaround, it adds a proxy layer) or to explore casually with
curl— a public API's consumers are typically third parties who benefit from JSON's simplicity, human-readability, and universal tooling support. - Browser-facing frontends — browsers don't support raw gRPC natively; you'd need gRPC-Web plus a translating proxy (e.g., Envoy), adding real infrastructure for a benefit (raw performance) that rarely matters for typical frontend-to-backend traffic.
- Simplicity and debuggability matter more than raw performance — a REST/JSON endpoint can be inspected with
curlor a browser's dev tools directly; a gRPC call requires a proto-aware client to even read the payload on the wire.
A common real-world pattern: use gRPC between your own internal services, and expose a REST/JSON (or GraphQL) API at the edge for browsers and third-party integrators — sometimes literally translating one to the other with a gateway.
Common mistakes
- Choosing gRPC for a public-facing API primarily because it's "faster," while ignoring that it's much harder for external consumers (and their tooling) to work with than plain JSON.
- Assuming gRPC requires abandoning REST entirely — most real architectures use both, gRPC internally and REST/JSON at the public edge.
- Forgetting gRPC needs HTTP/2 end-to-end — some older proxies, load balancers, or corporate networks don't support HTTP/2 well, which can cause confusing connectivity issues if not accounted for in infrastructure.
Interview questions
Q: Why is gRPC generally faster than a typical REST/JSON API? It serializes messages as compact binary protobuf instead of text-based JSON (smaller payloads, cheaper to encode/decode), and it runs over HTTP/2, which multiplexes many concurrent requests over a single connection instead of opening a new connection per request as older HTTP/1.1 REST clients often do.
Q: Why wouldn't you expose a public API directly as gRPC? Browsers can't speak raw gRPC natively (it needs gRPC-Web plus a translating proxy), and third-party API consumers generally benefit from JSON's simplicity, human-readability, and the near-universal tooling (curl, Postman, browser dev tools) built around plain HTTP/JSON. Public APIs typically expose REST/JSON even when the internal services behind them talk gRPC to each other.
Q: What problem does defining a service in a .proto file solve? It gives every language a single, shared, versioned source of truth for exactly what methods a service exposes and what its request/response messages look like — client and server code for every language is generated from that one file, removing the risk of a hand-maintained client SDK silently drifting out of sync with the actual server contract.