Meeting GraphQL
GraphQL is a query language for APIs, and a runtime that answers those queries. The client writes down which fields it needs, and the server returns those fields and nothing else.
Two problems: over-fetching and under-fetching
REST is not broken. But when the server decides the shape of every response in advance, it has to guess what each client needs. The examples use the blog API from chapter 04.
2012: rewriting the Facebook mobile News Feed
Facebook decided to rebuild its slow mobile News Feed as a native app. Mobile networks then were 2G and 3G: one round trip often took several hundred milliseconds, and data was billed by the kilobyte. Two things showed up in every profile. The REST responses carried many fields the app never displayed. And one screen needed several endpoints, called one after another.
Lee Byron, Nick Schrock, and Dan Schafer designed a different arrangement: the client sends a list of the fields it wants, and the server returns exactly those fields. Facebook released it as open source in 2015 under the name GraphQL, and moved the specification to the neutral GraphQL Foundation in 2018. The latest ratified edition is the September 2025 Edition, four years after the previous one.
The first problem is over-fetching: the response carries fields the client did not need. A post page shows the author's name and picture. But GET /users/9 returns the whole user record, because the server fixed that shape long before your page existed.
And that is one author. A list of 20 posts means 20 responses like this one. On a mobile connection, the greyed-out lines still cost data, battery, and waiting time.
Click any line on the left to change which fields you use, and watch the percentage move.
The second problem is under-fetching: one endpoint does not return everything the screen needs, so the client sends another request. Often the second URL cannot even be built until the first response arrives, so the requests run one after another instead of in parallel.
This is not a mistake by the API designer
You could add a parameter to /posts/1 that includes the author and the comments. That works. But every new screen then needs another variant, and the endpoint list keeps growing. The cause is structural: the server fixes the response shape in advance, so it can only guess. Return more and some clients waste bytes. Return less and some clients need another round trip.
The GraphQL answer: state the whole request once
The same post page. On the left, three REST requests. On the right, one GraphQL query.
Now the rule that makes GraphQL easy to read: the response mirrors the shape of the query. Compare the two windows line by line.
Why this rule is worth memorising
When you read the query, you already know the shape of the JSON you will receive. You do not have to open the documentation or print the response to find out. Only two things are added: an outer data object, and list fields such as comments come back as arrays.
Build a query yourself
On the left are the fields of the Character type from the Rick and Morty API. Select fields, and the query in the middle and the response on the right follow your selection.
origin lines up too. You asked for these fields, so you got these fields.Who decides the size of the response
In REST, that decision belongs to the server; a client can only ask for a different endpoint. In GraphQL the client decides. A list screen can select name and image. A detail screen can select all seven fields. Same API, same endpoint, two different queries.
One endpoint instead of many URLs
After the REST chapters you think in URLs. A GraphQL API usually exposes a single path.
/posts/posts/posts/1/posts/1/posts/1/comments/users/9/graphqlEvery request goes to this one path. The schema behind it defines what can be asked. The question changes from "which URL do I call" to "which fields do I select".
So where did the routes go? They did not disappear. They moved.
| In REST | In GraphQL |
|---|---|
| Many endpoints (URLs) | One endpoint, /graphql |
| A resource, located by its URL | A type, defined in the schema |
| Which endpoint, and which method | Which fields to select in the query |
| OpenAPI documentation, written separately | The schema, readable through introspection (chapter 08) |
Three operation types
Everything sent to that endpoint is one of three operation types. A query reads data. A mutation writes data; the specification requires that top-level mutation fields execute one after another, while the fields of a query may execute in parallel. A subscription asks the server to push updates as they happen, over a connection that stays open — usually a WebSocket, not this POST endpoint. Chapter 09 covers all three.
Open DevTools, and a GraphQL request looks like this on the network.
GraphQL travels over an ordinary HTTP POST
There is no new protocol here. The request is a normal POST. The body is JSON. The query is a string inside that JSON. GraphQL does not replace HTTP — it is carried by it. Everything you learned about fetch, headers, and DevTools still applies.
One thing does change, and it catches people who arrive from the HTTP and REST chapters. Look at the status line of a response where something went wrong.
200 OK. The author field is null, and the reason sits in errors with the path that failed. The rest of the data is still there and still usable.200 does not mean the query succeeded
A GraphQL server commonly answers 200 OK even when part of the query failed. The result is one JSON object that may contain both data and an errors array. So res.ok tells you that the HTTP request arrived and came back — not that the query worked. Read errors.
What happens to the data? A field that fails becomes null, and the server keeps executing the rest of the query. If that field was declared non-null in the schema, null is not allowed there, so the null moves up to the nearest parent field that does allow it. That is why a single broken field can empty a whole branch of the response, but not the whole response. Chapters 09 and 10 go further.
GraphiQL: a browser console for a GraphQL API
The extra i stands for graphical. GraphiQL is the standard in-browser editor for GraphQL, and it is where you will practise.
Where do the autocomplete and the documentation come from?
A GraphQL server can describe its own schema. The client sends a query that asks for the list of types and fields, and the server answers with it like any other query. That mechanism is called introspection, and chapter 08 covers it. REST has no equivalent in the protocol: documentation such as OpenAPI is written and published separately, and can drift away from the code.
One caution: many production servers turn introspection off, because it also tells an attacker exactly what the API contains. If a playground says introspection is disabled, that is usually deliberate.
Two public playgrounds need no account and open right now: rickandmortyapi.com/graphql and countries.trevorblades.com. The practice tasks below use both.
GraphQL does not replace REST
GraphQL changes who decides the shape of the response. It does not delete the problems that decision was solving.
Moving that decision to the client has a price. Three items on the bill are worth knowing now, even though chapter 10 is where you learn to handle them.
An HTTP cache keys entries on the URL and the method. Every GraphQL request is a POST to the same path, so a proxy or CDN has nothing to key on and cannot reuse responses the way it reuses GET resources. GraphQL clients solve this themselves: they keep a normalized cache, storing each object once under a key built from its id and its __typename.
That is the feature, and also the risk: a deeply nested query can be very expensive to execute. Servers add a maximum depth and a complexity budget, and reject queries above it. Many teams go further with persisted queries: the server accepts only a fixed set of queries it was given in advance, and the client sends an identifier instead of the text. Note that none of this is authorization — GraphQL provides none. Deciding who may read which field is still your own code.
On the server, each field is produced by a function called a resolver. Written in the obvious way, the resolver for author runs one database query per post: 1 query for 20 posts, then 20 more for their authors. The standard fix is batching. A loader collects every id requested during one tick of the event loop, fetches them in a single query, and hands each resolver its own row. DataLoader is the common implementation.
Several clients that each need different fields, such as iOS, Android, and web. One screen that combines data from several sources. A front-end team that needs to change what it requests without waiting for a new endpoint to be built.
A public API with one kind of client, content that many people read and few people change, file uploads and downloads, and anything you want a CDN to cache. These are exactly the cases where GET plus HTTP caching is already the answer.
How this course treats it
The next three chapters cover GraphQL properly: the schema as a contract (chapter 08), the three operations (chapter 09), and servers and performance (chapter 10). The finale then puts REST and GraphQL side by side and gives you a decision guide. Learn both before you choose one.
Practice
Three tasks. Two use public playgrounds, and the last one sends a query with fetch and nothing else.
Quiz
Nine questions. Every wrong option has its own explanation.
Why did Facebook build GraphQL internally in 2012?
Which of these are examples of over-fetching? (Select all)
Which statement about the relationship between a GraphQL query and its response is correct?
REST exposes many URLs. A GraphQL API conventionally exposes one endpoint, and its path is usually ____ (starts with a slash, then seven lowercase letters).
What does a typical GraphQL request from a browser look like?
A query asked for a post and its author. The author service was down. What does the response usually look like?
What is GraphiQL?
Who maintains the GraphQL specification today, and what is the latest ratified edition?
Which statement about the relationship between GraphQL and REST is most accurate?
- GraphQL is a query language for APIs and a runtime that executes those queries. It exists because a fixed response shape causes two problems: over-fetching (fields the client did not need) and under-fetching (extra round trips to assemble one screen).
- The client selects the fields, and the response mirrors the shape of the query. There is no
SELECT *— every field must be named. - By convention there is one endpoint,
POST /graphql. Resources become types defined in the schema, and choosing an endpoint becomes choosing fields. - It is carried over ordinary HTTP, so
fetchis enough to send one. But a GraphQL server commonly answers200 OKeven when a field failed: that field becomesnulland the reason goes intoerrors. Checkerrors, notres.ok. - The trade-off is real: one POST endpoint cannot use HTTP caching, the server must limit query depth and cost, and naive resolvers cause N+1 queries. 93% of teams work with REST APIs and 33% work with GraphQL (Postman, 2025). The finale compares the two.