This decision gets made on taste more often than on evidence, and both sides overstate their case. GraphQL is not automatically modern and REST is not automatically simple. The useful frame is consumer shape: how many different clients you have, how varied their data needs are, and how quickly you can ship changes when they need something new.
01The problem GraphQL actually solves
GraphQL exists because a mobile team needed six fields and the REST endpoint returned sixty, or because rendering one screen required four sequential round trips. When you have several clients with genuinely different data requirements and a backend team that cannot turn around a new endpoint quickly, letting the client specify its own shape removes a real bottleneck.
If you have one web client, a backend team that ships daily, and endpoints shaped for your screens already, that bottleneck does not exist. Adopting GraphQL to solve a problem you do not have means paying for a schema layer, a caching story and an authorisation model you did not previously need.
| Situation | Leans | Why |
|---|---|---|
| Public B2B API, many unknown consumers | REST | Cacheable, predictable, familiar tooling |
| Several first-party clients, varied needs | GraphQL | Removes per-client endpoint churn |
| Mobile app on poor networks | GraphQL | Fewer round trips, smaller payloads |
| Partner integrations, contractual SLAs | REST | Simpler to version and rate limit |
| Internal service-to-service | Neither, usually | gRPC is often the better fit |
02The costs nobody mentions in the pitch
Caching is the big one. HTTP caching does most of the work for a REST API and is largely unavailable to a GraphQL endpoint served over POST, so you rebuild it at the application layer. Rate limiting is similarly harder: requests are no longer uniform, so counting them tells you little and you need query cost analysis instead.
Authorisation gets harder too, because it moves from the endpoint to the field. Every resolver must independently answer whether this caller may see this field for this object, and a single missed check is a data leak that no endpoint-level test would catch.
03N+1 is not a rumour
A nested query that looks trivial - fifty orders, each with a customer - becomes fifty-one database queries unless you have batching in place. Dataloader-style batching is the standard answer and it works, but it is something you must add deliberately and keep working as the schema grows.
Depth and complexity limits matter for the same reason. Without them, a single deeply nested query from a curious consumer can generate enough load to take out your database, and it will arrive as one innocuous-looking POST that your rate limiter counted as a single request.
| Guardrail | Prevents | Typical setting |
|---|---|---|
| Query depth limit | Deeply nested traversal | 7-10 levels |
| Complexity scoring | Expensive but shallow queries | Cost budget per caller |
| Batching (dataloader) | N+1 database queries | Per-request cache |
| Persisted queries | Arbitrary queries from clients | Allowlist in production |
| Field-level authz | Data leaks via resolvers | Enforced in every resolver |
04The hybrid that usually wins
In practice the split we recommend most often is REST for the public and partner-facing API, GraphQL as a backend-for-frontend behind your own clients. External consumers get something cacheable, rate-limitable and familiar; internal clients get the flexibility, and the field-level authorisation surface stays inside your own trust boundary.
This is less elegant than picking one, and considerably more practical. It also means the GraphQL layer can change quickly without any external contract implications, which is where most of its value comes from anyway.
05How to decide in an afternoon
Count your clients and their data needs. If one client shape covers most usage, use REST. Count how long a new endpoint takes to ship end to end. If it is days, GraphQL is solving a real problem; if it is hours, it is solving one you do not have.
Then be honest about operational readiness. GraphQL in production needs complexity limits, persisted queries, per-field authorisation and a caching strategy. A team that will not build those should ship REST and revisit the question when the constraint actually bites.
Topics
Lena Voss
Lead Architect · SyncTrix
Writes about the engineering decisions behind production systems - architecture, delivery and the trade-offs that only show up at scale.
Building something like this?
SyncTrix engineers AI, SaaS, platform and cloud systems for enterprises and high-growth teams. Tell us what you're shipping and we'll scope it with you.
Talk to an engineer