As apps grow, especially when multiple clients (web, mobile, third-party) hit the same API, two friction points emerge with plain REST: over-fetching (getting more fields than you need) and under-fetching (needing multiple round trips to assemble a view). GraphQL was designed specifically to fix both: a single endpoint where the client describes exactly the data it wants, and the response mirrors the shape of the query.
query {
books {
title # only this
author {
name # and this
}
}
}
Notice what you just read in the previous lessons, though: JSON:API gives REST the same two
powers. Sparse fieldsets are field selection; include is nested fetching. The real
differences lie elsewhere:
| REST + JSON:API | GraphQL | |
|---|---|---|
| Endpoints | Many URLs, one per resource | One URL; the query body decides |
| Field selection | fields[type]=... | Part of every query |
| Related data | include=author | Nested in the query |
| HTTP caching | GET URLs cache in browsers and CDNs for free | All POSTs to one endpoint — needs persisted queries to cache |
| Contract | Conventions in docs | Typed, introspectable schema with autocomplete tooling |
| Server cost | Predictable queries | Arbitrary queries; resolvers can hide a database N+1 (use DataLoader) |
The schema is also an access-control boundary: if a field isn't in the GraphQL schema, no client can ever request it. With REST, omitting a field is a per-endpoint decision.
Rule of thumb: REST with JSON:API conventions is hard to beat for public, cacheable, CRUD-shaped APIs. GraphQL earns its complexity when many differently-shaped clients consume the same rich data graph and you want one flexible contract instead of an endpoint per view.
Check your understanding
Check your understanding
Your GraphQL API answers one HTTP request per view, but the database logs show one query per book's author. What's happening?
Check your understanding