GraphQL vs REST Tradeoffs
An honest comparison of GraphQL and REST: where each one shines, where each one hurts, and how to choose without religion.
What you'll learn
- ✓The real differences between GraphQL and REST
- ✓How overfetching and underfetching map to each style
- ✓When schemas and typed clients pay off
- ✓Where REST is simpler and faster to ship
- ✓Operational tradeoffs in production
Prerequisites
- •You have built or consumed an HTTP API
GraphQL vs REST has become a tribal argument. It should not be. Both are good at different things, and the right pick depends on team shape, client diversity, and how much your data graph really benefits from being queried as a graph. This post tries to be honest about both.
The actual difference
REST exposes resources at URLs. You GET, POST, PUT, DELETE them. The server decides what each response contains. GraphQL exposes a single endpoint with a typed schema; clients send queries that describe exactly what fields they want, and the server returns just that shape.
The big shift is who decides the response shape. In REST, the server. In GraphQL, the client.
Mental model
REST:
GET /users/42 -> {id, name, email, ...everything}
GET /users/42/posts -> [{id, title, body, ...}]
(two round trips, fixed shapes)
GraphQL:
POST /graphql with:
{ user(id:42) { name posts { title } } }
-> {data: {user: {name, posts: [{title}]}}}
(one trip, exact shape) REST is many endpoints, each returning a fixed shape. GraphQL is one endpoint and many queries against a typed schema.
Where GraphQL wins
- Many clients with different needs. A mobile app wants three fields; a web app wants twelve; a partner API wants a different slice. With REST you build endpoints for each or overfetch. With GraphQL each client asks for its own slice.
- Highly connected data. If your domain is naturally a graph (users -> posts -> comments -> authors), traversing it in one query is much nicer than chaining REST calls.
- Strong typing across the stack. The schema is the contract. Codegen produces typed clients automatically.
- Evolvability without versioning. You add fields; old clients ignore them. You deprecate fields with metadata, not new URLs.
Where REST wins
- Simple CRUD. If your API is mostly resource-shaped and clients are mostly your own server-rendered pages, REST is less ceremony.
- HTTP caching. GET responses are cacheable by URL out of the box. GraphQL is POST by default, so CDN caching takes more work.
- Predictable performance. Each REST endpoint has known database queries. GraphQL queries are user-shaped, which makes worst-case analysis harder.
- File uploads and streaming. HTTP is already good at these; GraphQL needs extensions.
- Tooling at the edge. Rate limits per route, WAF rules, audit logs are easier when “what is being done” maps to a URL.
Operations, the part that gets forgotten
A GraphQL server is a query executor. Without care, a single client query can fan out into hundreds of database calls. You need DataLoader (or an equivalent) for batching, query complexity limits, depth limits, and persisted queries to stop ad-hoc traffic. None of this is hard, but all of it is work that REST does not require.
REST scales by giving each endpoint a budget. If /users is slow, you optimize that endpoint. In GraphQL, you optimize resolvers and field-level performance, which is more diffuse.
Hands-on: the same call, both ways
REST:
GET /users/42 HTTP/1.1
GET /users/42/posts?limit=5 HTTP/1.1
GraphQL:
query UserPage($id: ID!) {
user(id: $id) {
name
posts(limit: 5) { title }
}
}
The REST version is two requests. The GraphQL version is one. For a single-page render that needs both, GraphQL is fewer round trips. For a CDN serving the same user list to many clients, the REST GET /users cached at the edge will win.
A simple decision framework
Pick GraphQL when at least two of these hold: client teams outnumber server teams, you have multiple client surfaces with diverging needs, your data is genuinely graph-shaped, type safety end-to-end is a hard requirement.
Pick REST when: your API is mostly CRUD, you want CDN caching by default, your team is small, or your audit/security tooling is URL-based.
For internal services, gRPC often beats both. It is not a choice between two options, it is three.
Common pitfalls
- Choosing GraphQL because it is fashionable. The schema work, N+1 prevention, and query budgeting are real ongoing costs.
- Building “REST” that is actually RPC. If every endpoint is a verb, you have RPC; consider whether OpenAPI plus a typed client gives you most of what you wanted.
- Exposing a GraphQL schema directly to the public internet without depth limits or persisted queries. You will get traffic that runs your database into the ground.
- Treating a REST resource as a database row. Resources are domain concepts; “user with their last three orders” can be a single endpoint.
- Forgetting that GraphQL subscriptions need a websocket plus a state machine on both ends. It is more than REST polling.
Practical tips
- For GraphQL: use persisted queries in production, enforce depth and complexity limits, and use DataLoader from day one.
- For REST: lean on OpenAPI plus generated clients. You get most of GraphQL’s typing benefit without the schema engine.
- Mix freely. A REST API for public CRUD plus a GraphQL gateway for internal consoles is a common, sane setup.
- Measure latency and per-request fanout. Whichever style you pick, the numbers tell you when you have abused it.
Wrap-up
GraphQL is the right call when client diversity and graph-shaped data justify the operational overhead. REST is the right call when your API is resource-shaped and you want HTTP’s caching and tooling for free. Both are fine. Pick on evidence, not vibes.
Related articles
- GraphQL GraphQL Federation: A Practical Overview
Understand Apollo Federation: subgraphs, the gateway, entity references, and when to choose federation over a monolithic GraphQL schema.
- GraphQL GraphQL Rate Limiting Strategies
Why GraphQL rate limiting is harder than REST and what to do about it. Covers query complexity analysis, depth limits, cost-based budgets, and per-field throttling.
- GraphQL GraphQL Resolver Patterns Explained
Compare resolver patterns in GraphQL: thin resolvers, service layers, DataLoader batching, and error handling that scales.
- GraphQL GraphQL Schema Design Best Practices
Practical patterns for designing GraphQL schemas that age well: nullability, pagination, mutations, errors, and evolution without versioning.