REST vs GraphQL: Differences, Trade-offs and When to Use Each
REST and GraphQL compared with real request examples: data fetching, caching, versioning, errors, security and tooling — plus a simple rule for choosing.
· 6 min read
Contents
- The same data, two ways
- REST
- GraphQL
- Side-by-side comparison
- Where REST shines
- Simplicity and ubiquity
- HTTP caching for free
- Clear operations and status codes
- Good fit for simple, resource-shaped data
- Where GraphQL shines
- Many clients with different needs
- Deeply related data
- A strongly typed contract
- Evolving without versions
- The costs of GraphQL
- The costs of REST
- A simple rule for choosing
- What about gRPC and tRPC?
- Building either in Python
- FAQ
- Is GraphQL faster than REST?
- Is GraphQL replacing REST?
- Does GraphQL need a specific database?
- How are errors handled in GraphQL?
- Further reading
REST and GraphQL are the two most common ways to design a web API. REST organizes an API around resources and URLs; GraphQL exposes one endpoint and a typed schema that clients query for exactly the data they need.
Neither is universally better. This comparison shows how they differ in practice and when each one is the right call.
The same data, two ways
Imagine a blog app. The client needs a user's name and the titles of their three most recent posts.
REST
GET /users/42
GET /users/42/posts?limit=3&sort=-created_at
{ "id": 42, "name": "Ana", "email": "ana@example.com", "bio": "…", "created_at": "…" }
[
{ "id": 901, "title": "Hello", "body": "…long text…", "tags": ["…"], "created_at": "…" },
…
]
Two round trips, and both responses include fields the client does not need (email, bio, body).
GraphQL
query {
user(id: 42) {
name
posts(last: 3) {
title
}
}
}
{
"data": {
"user": {
"name": "Ana",
"posts": [{ "title": "Hello" }, { "title": "Second post" }, { "title": "Third" }]
}
}
}
One request, and the response has exactly the requested shape.
Side-by-side comparison
| REST | GraphQL | |
|---|---|---|
| Endpoints | Many (one per resource) | Usually one (/graphql) |
| Data shape | Defined by the server | Defined by the client's query |
| Over-/under-fetching | Common | Avoided by design |
| HTTP caching | Works naturally (GET + URLs) | Harder; needs client-side or persisted-query caching |
| Schema & types | Optional (OpenAPI) | Required and built in |
| Versioning | Often /v1, /v2 | Evolve the schema, deprecate fields |
| Errors | HTTP status codes | Often 200 OK with an errors array |
| File uploads | Simple (multipart) | Needs extra conventions |
| Learning curve | Low | Moderate |
| Tooling | Universal (curl, browsers, any HTTP client) | Excellent but specific (GraphiQL, codegen, client libraries) |
Where REST shines
Simplicity and ubiquity
Every language, tool and developer understands HTTP verbs and URLs. You can test a REST API with curl in seconds, and public APIs are easier to adopt when they are REST.
HTTP caching for free
GET /products/17 can be cached by browsers, CDNs and reverse proxies using standard headers like Cache-Control and ETag. For read-heavy public data, this is a big performance and cost advantage.
Clear operations and status codes
POST creates, PUT/PATCH updates, DELETE deletes, and status codes like 404 or 409 tell clients and monitoring tools what happened without parsing the body.
Good fit for simple, resource-shaped data
CRUD apps, webhooks, file storage, and service-to-service calls map naturally onto resources.
Where GraphQL shines
Many clients with different needs
A web app, a mobile app and a smartwatch app can each request exactly the fields they display, without the backend team building a custom endpoint for each screen.
Deeply related data
Screens that combine users, posts, comments, likes and notifications can load in one request instead of a waterfall of REST calls — a real benefit on slow mobile networks.
A strongly typed contract
The schema documents every type and field. Tools can generate TypeScript types and client code from it, and editors autocomplete queries. Breaking changes are visible before they ship.
Evolving without versions
Add new fields freely; mark old ones with @deprecated and monitor usage before removing them.
The costs of GraphQL
- Performance pitfalls: a naive resolver for
posts → authorcan trigger one database query per post (the N+1 problem). The standard fix is batching with a DataLoader-style utility. - Expensive queries: clients can request deeply nested data. Production servers need query depth and complexity limits, timeouts and rate limiting based on query cost, not just request count.
- Caching is harder: everything is a
POSTto one URL by default. Persisted queries and client caches (Apollo, Relay, urql) solve this, at the cost of more moving parts. - Monitoring is different: every request hits the same endpoint and often returns
200, so you need GraphQL-aware logging to see which operations are slow or failing. - Authorization per field: access rules must be enforced in resolvers, because any field can be requested in any combination.
The costs of REST
- Over-fetching and under-fetching: endpoints return fixed shapes, so clients get too much data or need several calls.
- Endpoint sprawl: special-purpose endpoints (
/dashboard-summary,/mobile/home) accumulate to serve specific screens. - Versioning pain: breaking changes often mean maintaining
/v1and/v2side by side. - Inconsistent design: without discipline, naming, pagination and error formats drift. An OpenAPI spec and a style guide help.
A simple rule for choosing
Choose REST if:
- The API is public or consumed by third parties.
- Data is resource-shaped and screens map closely to resources.
- HTTP caching via a CDN matters.
- The team is small and wants the least machinery.
Choose GraphQL if:
- Several frontends (web, iOS, Android) consume the same backend with different data needs.
- Screens combine many related entities.
- You want a typed contract between frontend and backend teams.
- You can invest in query cost limits, batching and GraphQL-aware monitoring.
Many organizations use both: REST for public APIs, webhooks and file uploads; GraphQL as a backend-for-frontend layer for their own apps.
What about gRPC and tRPC?
- gRPC uses Protocol Buffers over HTTP/2. It is fast and strongly typed, and popular for internal service-to-service communication, but less convenient directly from browsers.
- tRPC gives end-to-end type safety between a TypeScript backend and frontend without a schema language. Great when both sides are TypeScript in one repository; not suitable for public or multi-language APIs.
Building either in Python
FastAPI makes REST APIs with automatic OpenAPI docs straightforward (see FastAPI vs Flask vs Django), and libraries such as Strawberry add GraphQL support to FastAPI and Django.
FAQ
Is GraphQL faster than REST?
Not inherently. It can reduce the number of requests and the payload size, which helps on slow networks. But a poorly optimized GraphQL server can be slower than an equivalent REST API, and REST benefits more easily from HTTP caching.
Is GraphQL replacing REST?
No. GraphQL is widely adopted for app backends, but REST remains the default for public APIs, integrations and simpler services.
Does GraphQL need a specific database?
No. GraphQL is a query language for your API, not your database. Resolvers can fetch data from SQL, NoSQL, other APIs or anything else.
How are errors handled in GraphQL?
A response can contain both data and an errors array, so partial success is possible. HTTP status is often 200 even when some fields fail, which is why clients must always check errors.
Further reading
Keep reading
SQLite vs PostgreSQL: Which Should You Use for Your Project?
An honest comparison of SQLite and PostgreSQL covering performance, concurrency, features, hosting and backups — with a simple rule for choosing.
FastAPI vs Flask vs Django: Which Python Framework to Choose
A practical comparison of FastAPI, Flask and Django: the same endpoint in each, performance, async, admin, ORM, learning curve and which fits your project.
Monorepo vs Polyrepo: Pros, Cons and How to Choose
Monorepos and polyrepos compared: code sharing, atomic changes, CI speed, ownership, tooling and the team sizes where each one works best.