Back to Blog

Product Management 7 min read

GraphQL queries, mutations and errors: how a GraphQL conversation works

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
    }
  }
}
  1. Operation typequery means "I want to read". The others are mutation and subscription, below.
  2. Operation nameOptional, but it makes the query easy to find in logs and monitoring.
  3. Starting pointWhere in the schema to begin. Here, one country.
  4. ArgumentWhich one. Like the parameters after the ? in a REST address.
  5. 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

query

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.

mutation

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.

subscription

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.

All three are sent to the same address. The first word of the operation says which kind it is.

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.

Many GraphQL APIs answer 200 OK even when something went wrong, because the request itself arrived safely. Monitoring that only counts status codes, as it would for REST, can miss every one of these.

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.

Published 06 Nov 2026 Updated 06 Nov 2026