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.

Protobuf
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:

Go
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 .proto file 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 .proto file 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 curl or 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.