REST, GraphQL or gRPC: what are the client-side trade-offs?
Predict first
Many screens need slightly different subsets of the same data and mobile round trips are costly. Which style addresses that most directly?
// A: REST with more endpoints
// B: GraphQL, where the client states the fields per query
// C: gRPC streaming
- A
- B
- C
Mental model. REST is ordering from a fixed menu of set dishes. GraphQL is telling the kitchen exactly the ingredients you want on one plate. gRPC is a dedicated hot-line between two kitchens that speaks a compact shorthand.
REST models resources at URLs and uses HTTP verbs and status codes. For a client it is simple, uses plain URLSession and Codable, benefits fully from HTTP caching (URL-keyed GETs, ETags) and is easy to debug (curl, proxy tools). Its costs: over-fetching (a fixed response shape sends fields the screen does not need), under-fetching (a screen needs several endpoints, so several round trips and waterfalls), and versioning. GraphQL exposes one endpoint and lets the client state the fields it wants in a query, so one request can shape exactly what a screen needs, and a typed schema gives code generation (Apollo iOS generates Swift types from queries). Client costs: caching moves into the client (a normalised cache keyed by object id), since almost everything is a POST to one URL and HTTP caches are no help; a heavier dependency and build step; harder error handling (a 200 response can contain an errors array, partial data included); and queries that can be expensive for the server, so it needs depth and cost limits.
gRPC uses Protocol Buffers over HTTP/2 with generated clients and supports streaming in both directions. Strengths: compact binary payloads, strict contracts, and first-class streaming for realtime features; costs: it is not natively supported by URLSession for all features (you use a library such as grpc-swift, which brings its own HTTP/2 transport), needs a gateway for browsers, is harder to inspect, and does not use HTTP caching. Rule of thumb: REST for public or simple resource APIs and when caching and tooling matter; GraphQL when many different clients and screens need flexible, aggregated data and the team can support it; gRPC for internal service-to-service and streaming-heavy or latency-sensitive paths. Whatever the choice, hide it behind your own protocol (a repository or service interface returning domain types), so screens do not depend on the wire format, and migrating or mixing styles is a change in one layer. Decide by measuring round trips, payload size and developer time, not fashion.
GraphQL lets each screen ask for exactly the fields it needs in one request, which addresses both over- and under-fetching.
| REST | GraphQL | gRPC | |
|---|---|---|---|
| Shape | fixed responses per URL | client chooses fields | defined messages and services |
| Wire format | JSON (usually) | JSON | Protocol Buffers (binary) |
| HTTP caching | works well | little help (POST) | none |
| Streaming | not built in | subscriptions | built in, both directions |
| Client tooling | URLSession + Codable | code generation (Apollo) | generated clients (grpc-swift) |
Common traps
- Choosing GraphQL to avoid writing endpoints, then losing HTTP caching. You must build and maintain a client-side cache.
- Letting generated wire types leak into the UI. Map them to domain models at the boundary.
They might follow up with
- Why is caching harder with GraphQL? Requests are POSTs to one URL, so URL-based HTTP caches cannot key them.
- Where does gRPC fit best? Internal services and streaming, rather than public APIs for browsers.
Related questions
- 12.3.1 How do you decode and paginate an API response, and which pagination style is safest?
- 12.3.2 How does HTTP caching work with Cache-Control, ETag and conditional requests, and how does URLCache fit in?
- 12.6.2 How would you design a networking layer shared by many apps and teams: interceptors, mocking, and URLProtocol?
How would you design a networking layer shared by many apps and… →