Same JSON, different philosophy
REST and GraphQL both return JSON, but they take opposite approaches to *who decides the shape*. In REST, the server defines fixed endpoints and response shapes. In GraphQL, the client asks for exactly the fields it wants in a single query. Understanding the JSON each produces clarifies the trade-offs.
REST: server-defined responses
A REST endpoint returns a fixed shape regardless of what the client needs:
{
"id": "usr_91",
"name": "Ada Lovelace",
"email": "ada@example.com",
"address": { "city": "London", "country": "UK" },
"createdAt": "2026-01-01T00:00:00Z",
"lastLogin": "2026-05-30T08:00:00Z"
}If the client only needs the name, it still receives everything — over-fetching. If it needs the user *and* their latest orders, it must call a second endpoint — under-fetching (the N+1 round-trip problem).
GraphQL: client-defined responses
The client sends a query describing exactly what it wants:
query {
user(id: "usr_91") {
name
orders(last: 3) { id total }
}
}And gets back JSON shaped like the query, nothing more:
{
"data": {
"user": {
"name": "Ada Lovelace",
"orders": [
{ "id": "A1", "total": 42 },
{ "id": "A2", "total": 9 }
]
}
}
}One request, exact fields, related data included. Note the top-level data envelope — a GraphQL convention.
Error handling differs
REST leans on HTTP status codes: 404 for not found, 422 for validation, 500 for server errors, with details in the body. GraphQL almost always returns 200 OK and puts problems in a top-level errors array:
{
"data": { "user": null },
"errors": [
{ "message": "User not found", "path": ["user"], "extensions": { "code": "NOT_FOUND" } }
]
}This means GraphQL clients must inspect the body, not just the status code.
Side-by-side
| Aspect | REST | GraphQL |
|---|---|---|
| Response shape | Fixed by server | Chosen by client |
| Over/under-fetching | Common | Avoided |
| Round-trips for related data | Often several | One |
| Errors | HTTP status codes | errors array, usually 200 |
| Caching | Easy (HTTP/URL based) | Harder (single endpoint) |
| Learning curve | Low | Higher |
When to choose which
- REST shines for simple, resource-oriented APIs, public APIs that benefit from HTTP caching and CDNs, and file or binary endpoints.
- GraphQL shines for rich client apps (especially mobile) that need many related resources with minimal round-trips and varied field requirements.
Many teams run both: REST for simple public endpoints, GraphQL for the data-hungry app frontend.