REST vs GraphQL: A Practical Comparison
GraphQL doesn't make REST obsolete — it solves a specific problem REST has, and creates a couple of its own. Here's the honest tradeoff.

REST vs GraphQL debates usually skip straight to "GraphQL is more flexible" without explaining what problem that flexibility is actually solving, or what it costs. Here's the practical version.
The core problem GraphQL solves: over-fetching and under-fetching
With REST, an endpoint returns a fixed shape of data. If a mobile screen needs only a user's name and avatar, but the /users/:id endpoint returns 20 fields, you're over-fetching — sending data you don't need.
If a screen needs a user's profile and their recent posts, and those live at two different REST endpoints, you're making two round trips — under-fetching from any single request.
query {
user(id: "123") {
name
avatar
posts(limit: 5) {
title
}
}
}
One GraphQL request, exactly the fields needed, from both the user and their related posts, in a single round trip.
What REST still does better
- Caching: REST endpoints map naturally to HTTP caching (
GET /users/123is cacheable by URL). GraphQL typically uses a singlePOSTendpoint, which breaks standard HTTP caching — you need additional tooling (Apollo's cache, persisted queries) to get comparable caching behavior. - Simplicity for simple APIs: if your API genuinely has a handful of resources with predictable access patterns, REST's conventions (GET, POST, PUT, DELETE) are immediately understandable without a query language to learn.
- File uploads and simple operations: REST handles these more naturally; GraphQL requires additional conventions/libraries to handle file uploads well.
- Debugging with basic tools: a REST endpoint can be tested with
curlor a browser URL bar. GraphQL generally needs a client that can construct the query.
What GraphQL genuinely does better
- Frontend teams get exactly the data shape they need, without backend teams needing to create new endpoints for every new UI requirement.
- A single request replaces multiple round trips for related, nested data — genuinely valuable for mobile apps on slower connections.
- Strong typing via the schema — the API's shape is explicit and tooling (autocomplete, validation) is built around it.
A practical decision framework:
| Situation | Reach for |
|---|---|
| Simple CRUD API, predictable access patterns | REST |
| Multiple client types (web, mobile) with very different data needs | GraphQL |
| Heavy nested/related data fetched together often | GraphQL |
| Need standard HTTP caching without extra tooling | REST |
| Small team, small API surface | REST — GraphQL's setup cost isn't worth it yet |
The honest complexity GraphQL adds
- N+1 query problems: naively resolving nested GraphQL fields can trigger many separate database queries — this needs deliberate solving (DataLoader pattern or similar), not something you get for free.
- Query complexity/cost control: a client can technically request deeply nested, expensive data in one query — production GraphQL APIs need query depth/complexity limits to prevent abuse.
- A steeper backend learning curve: schema design, resolvers, and the N+1 problem are all new concepts for a team used to REST controllers.
Try this yourself: if you have an existing REST API, pick one screen in your app that currently makes 2-3 separate REST calls to render. Sketch out what a single GraphQL query for that exact screen would look like. That exercise makes the over/under-fetching problem concrete instead of abstract.
Takeaway: GraphQL isn't a strict upgrade over REST — it trades HTTP-native caching and simplicity for flexible, precise data fetching, at the cost of new backend complexity (N+1 queries, query cost control). Pick based on whether your actual data-fetching pain (multiple client types, heavy nested data) matches what GraphQL solves.





