gRPC vs REST
See what a binary contract between two services buys you and what it costs in reach, so the choice follows your callers rather than the fashion.
gRPC supplies an RPC service contract, generated clients and explicit streaming call types; REST is an architectural style commonly implemented with HTTP and JSON. Compare caller support, contract evolution, streaming and measured workload costs. Either can have schemas and generated clients.
One API describes named tasks with agreed request forms; another exposes resources through a uniform interface. Both can publish the forms and check them. The right choice depends on what callers can use and how the contract changes.
Most of what gets written about this choice compares numbers a reader has no way to check: gRPC is faster, REST is simpler, pick one. That framing skips the one property of your situation that actually decides it, and it has nothing to do with either technology’s top speed. It is who is going to call the service you have not built yet — a browser, a partner integrating from outside, or a team on the other side of the same deploy pipeline. Answer that first and the rest of the comparison mostly answers itself.
What the contract changes#
gRPC normally defines service methods and message types in a .proto file and generates client/server code. Protobuf binary fields carry numeric tags, not field names. REST is an architectural style, not a JSON encoding rule; this comparison uses the common HTTP/JSON implementation. JSON carries names, but names alone do not explain allowed values or business meaning. A REST API can publish a schema through OpenAPI and generate clients too.
Generated APIs can catch some source-level changes during a typed-language build. They do not prove that deployed old and new services interoperate, and dynamic-language clients may discover a changed API at runtime. Protobuf’s evolution rules distinguish these boundaries: keep field numbers stable and reserve removed numbers instead of reusing them. A name-only rename may preserve the binary value; assigning a new meaning to the old tag may still compile and silently mislead an old reader.
Change a field's contract
The old caller expects an order identifier. Its JSON field is order_id; its protobuf field is string tag 1. The server now changes its schema. Predict what the old caller reads.
JSON rename: the old order_id lookup is missing.
Protobuf rename with tag 1 retained: the old caller still reads ord-42.
A name-only rename can preserve protobuf binary compatibility while breaking a JSON key lookup. Enable JavaScript to compare field changes.
A semantic model of one string field and an old caller, not an actual encoder or network call. It excludes protobuf JSON mapping, unknown-field policy, schema validators, generated-code compile checks and business-version migrations.
Illustrative calls, with setup, credentials, timeouts and schema validation omitted. The HTTP example decodes JSON without validating its shape; a generated or validating client could do more. Neither call demonstrates business compatibility or safe retries.
Schema evolution needs compatible deployments or an explicit migration/versioning plan, whichever API style you choose. Regenerating clients alone does not upgrade callers already deployed. gRPC libraries can apply configured retry policies and limited transparent retries. Caller and server must agree which effects are safe to repeat; see idempotency keys and safe retries.
Who can actually call you#
A browser cannot call a gRPC service directly. gRPC’s messages travel inside HTTP/2 frames and depend on one part of HTTP/2 a browser’s own request APIs do not expose: the trailers that carry a call’s final status after the body has already streamed. Fetch and XMLHttpRequest give a script no way to read them, so a browser talking to a gRPC service needs something in front of it — a proxy, or middleware doing the same job in the same process — translating the call into something the browser can actually send and read. That layer is one more thing to deploy and keep working, not a rounding error.
HTTP/JSON has broad browser and request-tool support. gRPC callers need tooling that understands the protocol and message schema, but not necessarily a generated SDK: server reflection can let tools such as grpcurl discover the contract. Some partners already support gRPC well. Ask what their actual integration environment permits.
Native gRPC defines unary, server-streaming, client-streaming and bidirectional-streaming calls. Check the selected client: gRPC-Web support does not automatically include every native streaming mode. HTTP responses can stream too, for example through server-sent events; bidirectional browser designs often use WebSockets. Compare the needed communication shape, cancellation and backpressure rather than assuming HTTP means only a short response.
Choosing the encoding before the caller#
Consider a hypothetical browser API with small payloads and expensive database work. Smaller wire messages might save little of its overall latency, while a translation layer adds operational work. A different browser workload may benefit substantially. Measure representative payloads, tail latency, server cost and required tooling before attributing an improvement to the protocol.
Plan incident tooling as part of that decision: available schemas, reflection policy, request tracing and safe payload inspection. These are costs to investigate, not measured evidence that one protocol necessarily costs more than it saves.
Internal RPCs with supported clients and a useful streaming or generation model are strong candidates for gRPC; HTTP/JSON can also be a good internal choice. Retries, partial failure and timeouts apply to both. Continue with the distributed-systems path for those concerns.
| Situation | Take | Because |
|---|---|---|
| A browser calls it directly | REST | No proxy layer needed to be reachable. |
| Both ends are yours, called constantly | Evaluate gRPC | Generated RPC clients fit; measure actual costs. |
| A partner integrates against it | REST | Common HTTP tooling; publish a usable contract. |
| Streaming in both directions | gRPC | Native bidirectional RPC streaming; check client support. |
- Situation
- A browser calls it directly
- Take
- REST
- Because
- No proxy layer needed to be reachable.
- Situation
- Both ends are yours, called constantly
- Take
- Evaluate gRPC
- Because
- Generated RPC clients fit; measure actual costs.
- Situation
- A partner integrates against it
- Take
- REST
- Because
- Common HTTP tooling; publish a usable contract.
- Situation
- Streaming in both directions
- Take
- gRPC
- Because
- Native bidirectional RPC streaming; check client support.
IF YOU REMEMBER ONE THING
Start with caller support and the operations you need. Then compare compatibility, streaming and representative performance. A schema is useful in either API style; a successful compile does not prove compatibility between deployed versions.
Questions people also ask
5 QUESTIONSCan a browser call gRPC directly?
No. A browser's fetch and XMLHttpRequest APIs do not expose HTTP/2 trailers, which is where gRPC carries a call's final status after the response body, so reaching a gRPC service from a browser needs a translation layer in front of it — a proxy such as Envoy, or middleware doing the same job inside the same process. That arrangement has a name, gRPC-Web, but it is not something a browser can do on its own.
Is gRPC always faster?
No. Protobuf binary messages often save bytes, but payload shape, compression, connection reuse, implementation and server work all affect latency and throughput. Benchmark representative calls, including browser translation if required, rather than treating an encoding choice as an end-to-end speed guarantee.
What happens when the schema changes?
Check binary compatibility, generated source APIs and business meaning separately. Renaming a protobuf field while keeping its number and compatible type can preserve binary decoding; reusing a number for another meaning can silently corrupt interpretation even when both schemas compile. REST/JSON APIs can also use schemas, generated clients and compatibility tests.
Can you use both in one system?
Yes, and a lot of systems do exactly that: gRPC between internal services that call each other constantly and already share a release process, REST at the edge where a browser or an outside partner connects. Choosing one for internal traffic does not commit you to the same choice at the boundary a caller you do not control has to cross.
What about GraphQL?
It answers a different question. GraphQL lets a caller ask a single endpoint for exactly the fields it needs, which solves over-fetching from a browser — it does not change the wire encoding this comparison is about, and a GraphQL server still typically answers in JSON over ordinary HTTP, with the same reach a plain REST endpoint has.