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 | |
|---|---|---|
| Where requests go | A separate URL per resource: /books, /authors, /books/42. | One endpoint (e.g. /graphql); the query in the request body decides what you get back. |
| Picking fields | Opt in per type with a query param (fields[books]=title). Leave it off and you get every field. | Built in: every query spells out the exact fields it wants, every time. |
| Pulling in related data | Ask for it explicitly with include=author; it comes back alongside the main resource. | Nest it right in the query (author { name }) and it's fetched in the same round trip. |
| HTTP caching | Reads are GETs with stable URLs, so browsers, CDNs, and proxies cache them for free. | Everything is a POST to one URL, so HTTP caching doesn't apply on its own. You need persisted queries or a client-side cache. |
| How the API is defined | By convention and written docs: nothing enforces the shape. | A typed, introspectable schema; tools can autocomplete and validate a query before it's even sent. |
| Cost on the server | Each endpoint runs a known, fixed query, so the load is predictable. | Clients compose arbitrary queries, so a deeply nested one can fire off N+1 database calls unless you batch them (e.g. with DataLoader). |
The schema doubles as a security boundary. If a field isn't in the GraphQL schema, no client can ever ask for it. The rule lives in one place. REST has no single gate like this: whether a field is exposed is decided endpoint by endpoint, so it's easy to leak one by forgetting to strip it.
So which should you reach for? REST with JSON:API conventions is hard to beat for public, cacheable, CRUD-style APIs, the kind where each client wants roughly the same data. GraphQL earns its extra complexity when lots of clients each need a different slice of the same rich data, and you'd rather hand them one flexible contract than build a new endpoint for every view.
Check your understanding
One query per author in the DB
Your GraphQL API answers one HTTP request per view, but the database logs show one query per book's author. What's happening?