In Part 1, GraphQL was writing your own order on one slip: one address, and a list of exactly what you want. This part looks at that slip up close, lets you send one to a real API and then covers the part that catches teams out most often: what happens when it goes wrong.
Every request goes to one address
A REST API has an address for each thing, and the method (GET, POST and so on) says what you're doing. A GraphQL API has one address, usually ending in /graphql, and almost every request is a POST. What changes is the text inside it: the query.
That has a knock-on effect you'll hear about. With REST, you can see what a product is doing by reading the list of addresses it calls. With GraphQL, every call looks the same from the outside, and the detail is all in the query. Logs, monitoring and analytics need to look inside.
A query, part by part
Here's the query the country explorer sends for the United Kingdom, with each part labelled:
A query, part by part
query CountryFacts {
country(code: "GB") {
name
capital
languages {
name
}
}
}- Operation type
querymeans "I want to read". The others aremutationandsubscription, below. - Operation nameOptional, but it makes the query easy to find in logs and monitoring.
- Starting pointWhere in the schema to begin. Here, one country.
- ArgumentWhich one. Like the parameters after the
?in a REST address. - FieldsWhat you want back. Curly brackets open a related thing, so you can choose its fields too.
It reads almost like a sentence: "query country facts: for the country with code GB, give me its name, capital and the name of each language".
Try it
This sends your query to the Countries API, a free GraphQL API that needs no key. Change the country and the fields, send it, and compare the query with the answer.
Try it: write a query and send it
Pick a country and tick what you'd like back. The query updates as you go, and the button sends it to the real Countries API. Tick the misspelt field to see an error.
Two things to notice. Untick everything and the answer still has the name, because the query always asks for it. And whatever you tick, the answer never has a field you didn't ask for.
Three kinds of operation
REST uses verbs (GET, POST, PUT, PATCH, DELETE) to say what kind of thing you're doing. GraphQL has three operation types, and the first word of the request says which:
Three kinds of operation
Read something
Ask the waiter what's on today
Show a product, an order's status, a country's capital. The REST equivalent is GET. The demo in this series only uses queries.
Change something
Place, change or cancel an order
Add to basket, update an address, cancel a booking. One word covers what REST splits into POST, PUT, PATCH and DELETE, and it can ask for fields back, such as the new basket total.
Be told when it changes
"Let me know when my table's ready"
Live scores, chat messages, a delivery moving on a map. Close in spirit to a webhook, but sent to an app that keeps a connection open.
When it goes wrong, look inside
This is the part to remember. With REST, the status code tells you how it went: 200 for fine, 404 for not found, 500 for a broken server. GraphQL APIs often answer 200 OK whatever happened, and put any problems in an errors list next to the data. An answer can also be partly right: some fields filled in, others null, with an error explaining each gap.
Pick each case to see the answer, and what the customer should see:
When it goes wrong, look inside
200 OK Everything came back. There's a data section and no errors.
{
"data": {
"product": {
"name": "Walnut desk",
"price": 349,
"reviews": [{ "stars": 5 }, { "stars": 4 }]
}
}
}The customer sees the product, its price and its reviews.
200 OK Some of it came back. The reviews service was down, so reviews is null and an errors list says why.
{
"data": {
"product": {
"name": "Walnut desk",
"price": 349,
"reviews": null
}
},
"errors": [{
"message": "Reviews are unavailable",
"path": ["product", "reviews"]
}]
}The customer should see the product and price, with a short note where the reviews would be. Not an error page.
200 or 400 The question didn't match the schema, so nothing ran. No data at all, just the reason.
{
"errors": [{
"message": "Cannot query field \"populaton\" on type \"Country\"."
}]
}The customer shouldn't ever see this: it's a bug in the product's own query, and tools that check queries against the schema catch it before release.
The "partly wrong" case is a real product decision. If the reviews are down, should the product page still load? Almost always yes. But someone has to decide what goes in the gap, and the acceptance criteria should say so.
What this means for product managers
- Ask for the schema. It's the menu. Before a story goes into a sprint, check every field the design needs is there, and which of them are nullable.
- Design for null. Any field without a
!can come back empty. The design needs a state for each one that matters. - Write acceptance criteria for partial answers. "Given the reviews can't be loaded, the product page still shows the product and price, with a short note where the reviews would be."
- Check monitoring reads the errors list. A dashboard of status codes can show all green while customers see half-empty pages.
- Watch for deprecations. GraphQL APIs rarely publish a v2. Instead, fields are marked deprecated and later removed. Ask the team to flag any the product still uses, and put removal dates on the roadmap.
- Name your operations. Named queries, like
CountryFacts, make it much easier to find a slow or failing one in the logs.
Words you'll hear
Words you'll hear
- Schema
The full list of types and fields a GraphQL API offers, and how they connect.
"Is that in the schema yet?"
- Query
A request to read data, listing the fields you want.
"The product page is one query."
- Mutation
A request to change data: create, update or delete.
"Checkout is a mutation."
- Resolver
The code on the server that fetches the value for one field.
"The reviews resolver is slow."
- Nullable
A field that's allowed to come back empty (no ! in the schema).
"Capital is nullable, so design for it being missing."
- Deprecated
Still works, but marked for retirement. Tools warn anyone still using it.
"That field's deprecated: use priceWithTax."
- Variables
Values passed in alongside a query, so the same query works for any country or product.
"Pass the product ID as a variable."
- Fragment
A named set of fields reused across queries, like the fields every product card needs.
"Add it to the ProductCard fragment."
- Introspection
Asking a GraphQL API for its own schema. It's how tools build documentation automatically.
"Is introspection turned off in production?"
- Federation
Several teams' GraphQL services stitched into one schema.
"Reviews is its own subgraph in the federation."
Part 3 puts REST and GraphQL side by side: the pros and cons of each, when to choose which, and the questions to ask before your team decides.