How to test a GraphQL API

Send queries and mutations, handle the errors array, use introspection instead of guessing field names, and validate GraphQL responses properly.

Updated

REST and GraphQL solve the same problem in opposite ways. With REST you call many URLs and take whatever each one returns. With GraphQL you call one URL and state exactly which fields you want back. That single difference changes how you test it.

One endpoint, many queries

A REST API spreads its surface across dozens of paths. A GraphQL API exposes a single endpoint — usually /graphql — and the request body decides what happens. There are no resource URLs to memorise; there is a schema of types, and you ask for a shape carved out of it.

That means the interesting variation in your tests lives in the request body, not the URL. A GraphQL test suite is a set of queries against one endpoint, not a set of endpoints.

Send your first query

Point SchemaClient's GraphQL panel at the endpoint and send a query:

query {
  user(id: "42") {
    name
    email
  }
}

You get back exactly those fields and nothing else:

{
  "data": {
    "user": { "name": "Ada", "email": "ada@example.com" }
  }
}

Ask for a field that does not exist and the server rejects the whole query before running it — a class of typo that a REST API would happily return as undefined.

Variables keep queries reusable

Do not paste changing values into the query text. Pass them as variables, so the same query works for every input:

query GetUser($id: ID!) {
  user(id: $id) {
    name
    email
  }
}
{ "id": "42" }

This is the GraphQL equivalent of parameterising a REST request, and it is what lets you save one query in a collection and reuse it instead of editing text every time.

Mutations change data

Writing data uses the same mechanism with the mutation keyword:

mutation UpdatePlan($id: ID!, $plan: String!) {
  updateUser(id: $id, plan: $plan) {
    id
    plan
  }
}

Ask the mutation to return the fields it changed, so you can confirm the write in the same round-trip instead of firing a second query to check.

The 200 OK trap

Here is the part that catches everyone coming from REST. A GraphQL endpoint typically answers 200 OK even when your query failed. The failure is described in an errors array alongside — or instead of — data:

{
  "data": null,
  "errors": [{ "message": "User 42 not found" }]
}

A test that only asserts on the status code passes here while the query returned nothing. You have to assert on the body: check that errors is absent and that data holds what you expected. This is the same failure mode described in why a 200 OK response can still be broken, just built into the protocol by design.

Use introspection instead of guessing

You do not need a copy of the schema to start. Most GraphQL servers answer an introspection query that describes their own types and fields, and SchemaClient uses it to offer autocomplete as you type. Point it at an unfamiliar endpoint and the available fields appear — far faster than reading documentation that may be out of date.

Validate the response shape

Once a query is stable, lock its shape in with schema validation. GraphQL guarantees the types of fields you asked for, but it does not stop a resolver returning null where your app expects a value, and nullability is where GraphQL responses most often break consumers. Assert on it rather than trusting it.

New to API testing generally? Start with how to test a REST API — the request-and-inspect loop is the same, only the request body differs.

Frequently asked questions

Why does my GraphQL API return 200 even when there is an error?

By convention GraphQL is served over a single HTTP endpoint that returns 200 for anything it managed to parse and execute, and reports problems in an `errors` array in the JSON body. So the status line tells you the transport worked, not that your query succeeded — you have to read the body to know.

How do I test a GraphQL mutation?

A mutation is sent the same way as a query — a POST with a `query` field whose text begins with `mutation` — usually with the changing values passed as `variables` rather than inlined. Send it, then read both the returned object and the `errors` array, because a mutation can partially succeed.

Do I need the schema to test a GraphQL API?

Not up front. Most GraphQL servers support introspection, so a client can ask the endpoint to describe its own types and fields. SchemaClient uses that to give you autocomplete, which means you can explore an unfamiliar API without a copy of its schema.