The answer is never "always one or the other." It depends on your data shape, your clients, and your team's experience.
The fundamental difference
RESTWhat is rest?An architectural style for web APIs where URLs represent resources (nouns) and HTTP methods (GET, POST, PUT, DELETE) represent actions on those resources. says: "Here are the resources I have. Each one lives at a URL. Use HTTPWhat is http?The protocol browsers and servers use to exchange web pages, API data, and other resources, defining how requests and responses are formatted. methods to interact with them."
GET /api/users/42
GET /api/users/42/posts
GET /api/users/42/posts/7/commentsGraphQLWhat is graphql?A query language for APIs where clients specify the exact shape of data they need in a single request, avoiding over-fetching and under-fetching. says: "Here is my schemaWhat is schema?A formal definition of the structure your data must follow - which fields exist, what types they have, and which are required.. Tell me exactly what you need, and I will return it in one response."
query {
user(id: 42) {
name
email
posts {
title
comments {
body
author { name }
}
}
}
}REST gives you fixed shapes at fixed URLs. GraphQL gives you a flexible query language against a typed schema.
Over-fetchingWhat is over-fetching?Receiving more data from an API than the client actually needs - a common REST limitation that GraphQL is designed to solve. and under-fetchingWhat is under-fetching?Not getting enough data in a single API response, forcing you to make additional requests to assemble what you need.
Over-fetching
The endpointWhat is endpoint?A specific URL path on a server that handles a particular type of request, like GET /api/users. returns everything, avatar, bio, settings, preferences, when you only needed name and email.
// REST: You get everything whether you need it or not
const response = await fetch('/api/users/42');
const user = await response.json();
// user = { id, name, email, avatar, bio, settings, preferences, createdAt, ... }
// You only needed: name and emailUnder-fetching
You need a user's profile page showing their name, their 5 latest posts, and the comment count on each post. With RESTWhat is rest?An architectural style for web APIs where URLs represent resources (nouns) and HTTP methods (GET, POST, PUT, DELETE) represent actions on those resources.:
// REST: Three round-trips to build one page
const user = await fetch('/api/users/42').then(r => r.json());
const posts = await fetch('/api/users/42/posts?limit=5').then(r => r.json());
// Now you need comment counts for each post...
const commentCounts = await Promise.all(
posts.map(post =>
fetch(`/api/posts/${post.id}/comments?count=true`).then(r => r.json())
)
);That is 1 + 1 + 5 = 7 HTTPWhat is http?The protocol browsers and servers use to exchange web pages, API data, and other resources, defining how requests and responses are formatted. requests.
GraphQLWhat is graphql?A query language for APIs where clients specify the exact shape of data they need in a single request, avoiding over-fetching and under-fetching. solves both
query {
user(id: 42) {
name
email
posts(limit: 5) {
title
commentCount
}
}
}One request. Exactly the fields you need. No more, no less.
When RESTWhat is rest?An architectural style for web APIs where URLs represent resources (nouns) and HTTP methods (GET, POST, PUT, DELETE) represent actions on those resources. wins
Simple CRUDWhat is crud?Create, Read, Update, Delete - the four basic operations almost every application performs on data. operations
If your APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses. is mostly "create, read, update, delete," REST maps perfectly to HTTPWhat is http?The protocol browsers and servers use to exchange web pages, API data, and other resources, defining how requests and responses are formatted. semantics.
POST /api/articles → Create
GET /api/articles/123 → Read
PUT /api/articles/123 → Update
DELETE /api/articles/123 → DeleteHTTP caching
Because each resource has a unique URL, HTTP caching works out of the box, browsers, CDNs, and reverse proxies all cache GET requests automatically.
GET /api/articles/123
Cache-Control: public, max-age=3600
ETag: "abc123"GraphQLWhat is graphql?A query language for APIs where clients specify the exact shape of data they need in a single request, avoiding over-fetching and under-fetching. sends everything as POST to a single endpointWhat is endpoint?A specific URL path on a server that handles a particular type of request, like GET /api/users.. HTTP caching does not work, you need application-level caching (Apollo Client, urql).
Public APIs
REST is far easier to document and consume. Developers can test with curl, Postman, or a browser. GraphQL requires tooling (GraphiQL, Apollo Explorer) and schemaWhat is schema?A formal definition of the structure your data must follow - which fields exist, what types they have, and which are required. knowledge.
File uploads
REST handles file uploads natively with multipart/form-data. GraphQL has no built-in file upload support, you need workarounds like graphql-upload or a separate REST endpoint.
When GraphQLWhat is graphql?A query language for APIs where clients specify the exact shape of data they need in a single request, avoiding over-fetching and under-fetching. wins
Complex, nested data
When your data has deep relationships, users have posts, posts have comments, comments have authors, GraphQL lets clients traverse the graph in a single query.
Multiple frontends
A web app needs full user profiles. A mobile app needs just names and avatars. With RESTWhat is rest?An architectural style for web APIs where URLs represent resources (nouns) and HTTP methods (GET, POST, PUT, DELETE) represent actions on those resources., you build separate endpoints for each or accept over-fetchingWhat is over-fetching?Receiving more data from an API than the client actually needs - a common REST limitation that GraphQL is designed to solve.. With GraphQL, each client requests exactly what it needs from the same schemaWhat is schema?A formal definition of the structure your data must follow - which fields exist, what types they have, and which are required..
Rapid frontend iteration
Frontend teams can add new fields to their queries without waiting for backend changes, as long as the field exists in the schema.
Strongly typed contract
The GraphQL schema serves as a living contract between frontend and backend. Tools can auto-generate TypeScript types from the schema, catching integration errors at compile time.
The GitHub case study
GitHub RESTWhat is rest?An architectural style for web APIs where URLs represent resources (nouns) and HTTP methods (GET, POST, PUT, DELETE) represent actions on those resources. APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses. (v3): launched in 2011:
GET /repos/facebook/react/issues?state=open&per_page=5Returns full issue objects with all fields. To get labels and assignees, you parse the full response.
GitHub GraphQLWhat is graphql?A query language for APIs where clients specify the exact shape of data they need in a single request, avoiding over-fetching and under-fetching. API (v4): launched in 2017:
query {
repository(owner: "facebook", name: "react") {
issues(first: 5, states: OPEN) {
nodes {
title
labels(first: 3) { nodes { name } }
assignees(first: 1) { nodes { login } }
}
}
}
}Why did they add GraphQL? Their REST API required dozens of requests to render a single page. A pull requestWhat is pull request?A proposal to merge code changes into a shared branch, where teammates review the diff before it's accepted. page needed calls to the PR endpointWhat is endpoint?A specific URL path on a server that handles a particular type of request, like GET /api/users., reviews, comments, status checks, labels, and assignees, all separate resources. GraphQL collapsed that into one query.
They kept REST v3 alive because millions of CI/CDWhat is ci/cd?Continuous Integration and Continuous Deployment - automated pipelines that test your code on every push and deploy it when tests pass. scripts, bots, and integrations use it. For simple operations like "create an issue" or "merge a PR," REST is perfectly adequate.
Side-by-side comparison
| Criteria | REST | GraphQL |
|---|---|---|
| Data fetching | Fixed response shape per endpoint | Client specifies exact fields needed |
| Number of requests | Multiple endpoints = multiple requests | Single request for complex data |
| Caching | HTTP caching works natively (CDN, browser) | Requires application-level caching |
| Learning curve | Low, uses standard HTTP | Higher, query language + schema |
| Error handling | HTTP status codes (400, 404, 500) | Always 200; errors in response body |
| File uploads | Native multipart/form-data support | Requires workarounds or separate endpoint |
| Versioning | URL versioning (/v1/, /v2/) | Schema evolution (deprecate fields) |
| Tooling | curl, Postman, browser | GraphiQL, Apollo Explorer, codegen |
| Over-fetching | Common problem | Solved by design |
| Under-fetching | Common problem | Solved by design |
| Real-time | Requires WebSockets separately | Built-in subscriptions |
| API discovery | OpenAPI/Swagger docs | Introspection (schema is self-documenting) |
Making the decision
- RESTWhat is rest?An architectural style for web APIs where URLs represent resources (nouns) and HTTP methods (GET, POST, PUT, DELETE) represent actions on those resources. if: Your data is flat, you need HTTPWhat is http?The protocol browsers and servers use to exchange web pages, API data, and other resources, defining how requests and responses are formatted. caching, you are building a public APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses., your team knows REST well, or you are building simple CRUDWhat is crud?Create, Read, Update, Delete - the four basic operations almost every application performs on data..
- GraphQLWhat is graphql?A query language for APIs where clients specify the exact shape of data they need in a single request, avoiding over-fetching and under-fetching. if: Your data is deeply nested, you serve multiple frontends with different data needs, your frontend team wants to move independently, or you are building a product with complex data relationships.
- Both if: You have a large platform where some use cases are simple CRUD (REST) and others are complex queries (GraphQL). GitHub, Shopify, and Yelp all run both.