Compare REST vs GraphQL APIs.
Who controls the response shape
In REST the server decides the response shape, in GraphQL the client builds it, and in gRPC a compiled contract sets it. The trade-offs come from who decides the shape.
For this question · chapters 1 and 2 of 3
Over-fetching and under-fetching
- A profile screen needs its data in one exact shape. The screen has four rows: the name with its avatar, then the last three orders, each with a product name and a total. But the server that answers the request has never seen this screen. Every problem in this concept comes from the server not knowing that shape.
- The first request shows over-fetching: the response is bigger than the screen needs. The call to GET /users/123 returns 24 fields in 2400 bytes, but the screen shows only two of them. The mobile radio still turns on to download the other 22 fields, which the screen never shows. The endpoint is not badly written: it is written once for every caller, and this screen is only one of those callers.
- Under-fetching is the same mistake in the opposite direction. This time the fixed response shape is too small, because the user record holds order_ids, not the orders themselves. So the client has to send a second request. The second request cannot start until the first one has finished.
- On a phone, 610 ms means most of a second of blank screen. The screen waits for a third request to turn the product ids into names. The three requests could not run at the same time, because each request needed ids from the one before it. The three requests also moved 6900 bytes so the screen could show 8 of the 99 fields that came back.
- REST has a reasonable fix: GET /screens/profile, an endpoint that returns exactly the eight fields the profile screen shows. So the profile call takes one trip, 600 bytes and 250 ms. But the order screen and the settings screen each need their own endpoint, so every new screen means a deploy. A ?fields= parameter on the old endpoint does the same job with less setup, but the server still decides which fields exist.
- Speed was never the point, because the purpose-built endpoint was as fast as anything that comes after it. The real question is who decides the response shape. In REST, the server decides the shape, one endpoint at a time. The next two chapters give the other two answers.
The client builds the query
- GraphQL starts by putting the three REST paths behind one endpoint, POST /graphql. A schema behind that endpoint names every type and every field. The three REST paths still exist, and most GraphQL layers run in front of services that are still REST-shaped. So GraphQL does not replace REST.
- The main idea of GraphQL is that the client writes the response shape it wants. Each line of the query names a field that already exists in the schema. So the server can check the whole query before running any part of it. In this query, the server decided nothing.
- GraphQL is no faster than the purpose-built endpoint from chapter 1. GraphQL and that endpoint both cut three round trips to one, and 6900 bytes to 600. Both also cut the time from 610 ms to 250 ms. What changed is who decides the response shape. This time the client changed the shape, so nobody deployed anything.
- The cost is HTTP caching. In chapter 1, every GET was a cache key, so a CDN or browser could answer that GET without calling your servers. Now every query is a POST to one URL, /graphql, with the query in the request body, which a cache does not read. A GET transport with persisted queries gets some of that caching back, but this setup adds a build step.
- One HTTP request is not one database query, because the product field resolves once for every order. So three orders cost five queries, and fifty orders on a list screen would cost fifty-two. A batching loader collects the product lookups into one query, which cuts this N+1 problem to three queries. A flexible query means you now own a query planner.
- A depth limit alone was never enough. This query nests five fields, inside the depth limit of 8, but it costs a million nodes against a cost limit of 1000. The guard on the endpoint refuses the query with a 400 before any resolver runs, so the query never reaches your database. A client can build a query that crashes your database, so you need a cost budget too.
A profile screen needs its data in one exact shape. The screen has four rows: the name with its avatar, then the last three orders, each with a product name and a total. But the server that answers the request has never seen this screen. Every problem in this concept comes from the server not knowing that shape.
© LearnThatStack - diagrams may not be republished without permission.
Want a quick review of the fundamentals? See the API Design cheatsheet.
74 of 92 API Design answers are in Pro.
Full answers, code samples, and AI explanations that go simpler or deeper. Cancel anytime.
- Full answers + code
- AI explanations, simpler or deeper
- 1,000 AI credits / month
- Cancel anytime