1. The Over-Fetching & Under-Fetching Problem in REST
In traditional REST architecture, endpoints return fixed JSON shapes defined by the backend server. A mobile client calling `GET /api/v1/users/42` might receive 40+ user fields (address, billing, history) when it only needed the user's name and avatar URL. This wasted bandwidth is called over-fetching.
{
"id": 42,
"first_name": "Alex",
"last_name": "Rivers",
"avatar_url": "https://cdn.example.com/avatar.jpg",
"billing_address": { "street": "100 Main St", "zip": "90210" },
"internal_flags": { "is_verified": true, "risk_score": 0.02 },
"created_at": "2024-01-15T08:30:00Z"
}2. Declarative Client-Driven JSON Shapes in GraphQL
GraphQL eliminates over-fetching by allowing clients to submit a declarative query specifying the exact fields needed. The GraphQL engine executes the query and returns a JSON response matching the exact structure requested.
// 1. GraphQL POST Query Payload
// query { user(id: 42) { first_name avatar_url } }
// 2. Compact GraphQL JSON Response Payload
{
"data": {
"user": {
"first_name": "Alex",
"avatar_url": "https://cdn.example.com/avatar.jpg"
}
}
}3. Error Handling Schemas — HTTP Status Codes vs errors Array
REST APIs communicate error state using native HTTP status codes (400 Bad Request, 404 Not Found, 422 Unprocessable Entity). Conversely, GraphQL endpoints always return HTTP 200 OK and serialize errors inside a top-level `"errors"` JSON array.
{
"data": null,
"errors": [
{
"message": "User not found for ID 42",
"locations": [{ "line": 2, "column": 3 }],
"path": ["user"]
}
]
}