REST, GraphQL or gRPC: Matching the API Style to Each Surface
The REST versus GraphQL versus gRPC question usually gets decided once, for the whole company, based on whichever one a founding engineer used at their last job. That's a mistake in a system with more than one kind of API surface, because the three solve different problems and carry different operational costs. The better question isn't which one is best; it's which one fits each surface you actually have.
Where REST still wins
For a public or partner-facing API, REST's ubiquity is the point: every language has mature tooling for it, HTTP caching works the way engineers already expect, and a partner's engineer can debug an integration with curl and a browser instead of installing your specific client library. The cost is real too, mainly in over-fetching and under-fetching: a mobile client that needs six fields from a twenty-field resource still gets all twenty, or needs three separate calls to assemble one screen. For a stable, well-understood resource model consumed by many different clients you don't control, that cost is usually worth paying for the simplicity and debuggability.
Where GraphQL earns its complexity
GraphQL is worth its operational overhead when you have many client-driven view shapes over the same underlying data: a mobile app, a web dashboard, and a third-party integration all wanting different slices of the same resources, especially under variable network conditions where over-fetching has a real cost. The tradeoffs show up once it's live: the N+1 query problem, where a naive resolver fires one database query per item in a list, needs deliberate batching (data loaders or equivalent) to avoid quietly overwhelming the database. Query complexity also needs limits, because a client-composed query can be far more expensive than any single REST endpoint was ever designed to be, and an unbounded schema lets a client accidentally (or deliberately) request something that costs far more than expected to serve.
Where gRPC wins, and what it costs
For internal, service-to-service calls where you control both ends, gRPC's strict contracts (via protobuf), built-in streaming, and lower per-call overhead make it a strong default. The costs are mostly about who's on the other end of the call: browsers don't speak gRPC natively and need a proxy layer (gRPC-Web or similar) to bridge it, the binary protocol is harder to poke at with a generic HTTP tool when you're debugging, and there's a real onboarding cost for engineers who've only worked with REST or GraphQL before. None of that matters much for calls between your own backend services; all of it matters if you're exposing the same API to an external partner.
A decision rule by surface, not by preference
External, partner-facing API: REST with a clear, versioned contract, because the debuggability and ubiquity outweigh the over-fetching cost for most integration patterns. Multiple client shapes over one dataset, especially with mobile clients on variable networks: GraphQL, with query complexity limits from day one, not added after the first incident. Internal, high-throughput calls between services you own end to end: gRPC, for the contract strictness and lower overhead. The rule isn't about which technology is more advanced; it's about matching the API style to who's actually calling it and under what constraints.
Match the API style to the surface using these rules:
- For an external, partner-facing API, use REST with a clear versioned contract, since debuggability and ubiquity outweigh the over-fetching cost.
- For many client shapes over one dataset, especially mobile on variable networks, use GraphQL with query complexity limits in place from the start.
- For internal, high-throughput calls between services you own end to end, use gRPC for its strict contracts and lower overhead.
A mistake to avoid: running all three for a small surface
A small engineering team running REST for partners, GraphQL for the dashboard, and gRPC internally, each because it was the right theoretical fit for its surface, ends up maintaining three sets of tooling, three deployment and monitoring patterns, and three different debugging skill sets across an on-call rotation that might be five people. For a small team, standardizing on one and accepting a worse fit on the surface where it's not ideal is often the better tradeoff than being technically correct on all three fronts and operationally stretched across all of them. Save the multi-protocol setup for when the team is large enough to actually specialize.
For example, a small team with a partner API, a web dashboard and internal services might pick REST as its single standard. Over-fetching on the dashboard is then handled with targeted endpoints or field-selection query parameters, and internal calls stay on the same tooling everyone already knows. The team gives up a perfect fit on two surfaces and keeps one deployment pattern, one monitoring setup and one debugging skill set for the on-call rotation. Revisit the decision when the team is large enough for people to specialize, not before.
What Good Looks Like
A good API strategy assigns REST, GraphQL or gRPC per surface based on who's calling it and under what constraints, rather than standardizing one protocol company-wide regardless of fit.
Building The Capability (5-Stage Skill Ladder)
How to Get Started
Frequently Asked Questions
Can we run GraphQL as a layer in front of REST or gRPC services instead of picking one?
Yes, a GraphQL gateway in front of existing REST or gRPC services is common and lets internal services keep their own protocol while external clients get a unified, client-shaped query surface. It adds an extra hop and its own operational surface, so it's worth it mainly once you have enough client diversity to justify a dedicated gateway team.
Does gRPC's binary protocol make debugging production issues harder?
It removes the ability to just read a request with a generic HTTP tool, so teams need grpcurl or equivalent, plus good structured logging and tracing, to get the same visibility REST gives for free. That's a real cost worth planning for, not a reason to avoid gRPC for internal calls where the performance and contract benefits are worth it.
Is it worth migrating an existing REST API to GraphQL just to fix over-fetching?
Usually not on its own. If over-fetching is the only pain point, targeted endpoints or field-selection query parameters solve it with far less operational cost. Migrate to GraphQL when you actually have multiple, meaningfully different client shapes over the same data, not to solve a single endpoint's inefficiency.
About the numbers
This guide doesn't quote a sourced benchmark. Figures in it are estimates or general guidance, so check them against your own numbers.
Related Guides
REST, GraphQL, or gRPC: Choosing by Workload
REST, GraphQL, and gRPC solve different problems. A decision rule for which one fits a public API, a mobile client, or service-to-service calls.
gRPC, GraphQL, or REST: What Actually Breaks Each One at Scale
REST, GraphQL, and gRPC each fail differently under real production load. A comparison of the specific tradeoffs that matter once you are past a prototype.
GraphQL, REST, or gRPC: How to Actually Choose
Compare GraphQL, REST, and gRPC on client flexibility, caching, tooling, and internal versus external use, so you can pick the right API style.
GraphQL, REST, or gRPC: Match the Protocol to the Caller
REST for public APIs, gRPC for internal services, GraphQL for aggregation: a practical way to pick, plus the N+1 and schema-drift traps in each.
GraphQL, REST, or gRPC: Picking an API Style by Use Case
A side-by-side comparison of GraphQL, REST, and gRPC for internal and external APIs, with the tradeoffs that actually matter when choosing between them.
gRPC vs GraphQL vs REST: Picking the Right One Per Use Case
REST, GraphQL, and gRPC solve different problems. A practical decision guide for picking the right one for a public API, an internal service, or a client.