API Architecture · October 28, 2024 · 5 min read

Why I Reach for gRPC Before REST

The practical case for protocol buffers and gRPC in internal service communication — and when REST is still the right answer.

REST is not wrong. It’s the right default for public APIs consumed by third parties you don’t control, browsers, and mobile clients. But for internal service-to-service communication, it carries a set of costs that are easy to ignore until you have twenty services.

What REST Costs You Internally

Schema drift. JSON has no enforced schema at the transport layer. Field renames, type changes, and missing fields are runtime errors, not compile-time errors. In a multi-service system, this means contract breakage is invisible until something crashes in production.

Boilerplate. Every REST service needs its own serialization/deserialization logic, its own OpenAPI spec (maybe), its own client library (maybe). These aren’t hard problems, but they compound.

No streaming primitive. Server-sent events and WebSockets exist, but they’re workarounds. HTTP/1.1 was not designed for bidirectional streaming.

What gRPC Gives You

Contracts as code. A .proto file is the contract. It’s versioned, it generates client and server stubs in any language, and it’s checked at compile time.

syntax = "proto3";

service EventIngestion {
    rpc Publish(PublishRequest) returns (PublishResponse);
    rpc StreamEvents(StreamRequest) returns (stream Event);
}

message PublishRequest {
    string topic   = 1;
    bytes  payload = 2;
}

Run protoc and you get a type-safe Go client that handles serialization, connection pooling, and deadlines:

client := pb.NewEventIngestionClient(conn)
resp, err := client.Publish(ctx, &pb.PublishRequest{
    Topic:   "threat-events",
    Payload: data,
})

Bidirectional streaming. Server streaming, client streaming, and full-duplex streaming are first-class primitives. When I built the distributed storage replication layer, gRPC streaming eliminated the need for a separate WebSocket stack entirely.

Multiplexing over HTTP/2. Multiple in-flight RPCs over a single TCP connection, with backpressure and flow control built in.

When REST Is Still the Right Answer

  • Public APIs. Third parties expect REST. Forcing them to use protoc is a non-starter.
  • Browser clients. gRPC-web exists but adds complexity. For browser-facing APIs, REST or GraphQL is simpler.
  • Simple CRUD with no performance requirements. If you’re building an admin panel that does 10 requests per minute, the tooling overhead of gRPC is not worth it.
  • Teams unfamiliar with protobuf. The learning curve is real. Don’t introduce it without buy-in.

The Practical Rule

Default to gRPC for any new internal service. Default to REST for anything that crosses a trust boundary or needs to be consumed by a browser without additional tooling.

The schema enforcement alone is worth it. You will spend zero hours debugging a “field was renamed in service X but nobody updated service Y” incident, because it won’t compile.