REST and GraphQL solve the same problem — getting data to clients — in two different shapes. The honest answer to "which should I use" is almost never about which is more modern. It's about how many kinds of clients you serve, how much you rely on caching, and how much complexity you're willing to own. Here's the trade-off, without the sales pitch.
The core difference
REST models your API as resources at URLs, and the client takes whatever each endpoint returns. GraphQL exposes one endpoint and lets the client ask for exactly the fields it wants.
# GraphQL — the client declares the shape it needs
query {
user(id: "42") {
name
orders(last: 3) { total, status }
}
}
# REST — the server decides the shape; the client stitches calls together
GET /users/42
GET /users/42/orders?limit=3
That single difference drives every trade-off below.
Over-fetching and under-fetching
This is GraphQL's headline win. A REST endpoint returns a fixed payload, so a mobile screen that needs three fields still downloads all twenty (over-fetching), and a screen that needs data from three resources makes three round-trips (under-fetching). GraphQL collapses both: one request, exactly the fields asked for.
If you have one web client you control, this barely matters — you just shape the endpoint. It matters a lot when a dozen teams and a mobile app all read the same data differently and you'd otherwise grow a /users/42/summary-for-mobile endpoint for every screen.
Caching is where REST pulls ahead
REST rides on HTTP. A GET /users/42 is cacheable by browsers, CDNs, and proxies for free, keyed by URL, with ETag and Cache-Control. GraphQL sends everything as POST to one URL, so none of that works out of the box — you cache at the client (Apollo, urql, Relay) or add persisted queries. You don't lose caching with GraphQL, but you rebuild it yourself one layer up.
The cost GraphQL adds
Most comparisons skip this. Adopting GraphQL means owning:
- A schema and resolvers — a second type system to keep in sync with your data.
- The N+1 problem — nested resolvers fan out into one query per item unless you add a batching layer like DataLoader.
- Query-cost limits — a public GraphQL endpoint lets clients ask for deeply nested, expensive queries, so you need depth/complexity limits to avoid a denial-of-service by query.
- Harder observability — every request hits one endpoint, so "which query is slow" needs per-field tracing, not just per-route metrics.
None of these are dealbreakers. They're just real work that a handful of REST endpoints don't require.
A rule of thumb
- Reach for REST when you have a CRUD-shaped API, one or two clients you control, and you want cacheable, easily monitored endpoints. This is most projects.
- Reach for GraphQL when many diverse clients need different slices of the same graph of data, endpoint sprawl is already hurting, and you can invest in schema, batching, and cost limits.
- You can mix them — a GraphQL gateway over REST services, or REST for public/cacheable reads and GraphQL for a rich internal app.
Pick by the shape of your clients and your caching needs, not by which one trended last year.
FAQ
Is GraphQL better than REST?
Neither is better in general. GraphQL shines when many different clients need different shapes of the same data and you want to avoid endpoint sprawl. REST is simpler to build, cache, and monitor, and it's the right default for most CRUD APIs.
Does GraphQL replace a database or a REST API?
Neither. GraphQL is a query language for your API layer; it still calls the same databases and services underneath. You can even put a GraphQL layer in front of existing REST endpoints.
What is the N+1 problem in GraphQL?
A nested query can trigger one database call per item in a list — 1 query for the list plus N for its children. It's solved with a batching layer like DataLoader, but you have to add it deliberately; REST endpoints usually batch this in a single handler.

