GraphQL path · Execution · Topic 5 of 12

Errors: partial data and the errors array

Not started

A GraphQL response is a map with up to three keys: data, errors and extensions. If the request couldn't be parsed or failed validation, there is no data at all, only errors. If execution started, data is present, and any field whose resolver failed is set to null with an entry in errors describing it. The client therefore gets everything that worked plus a precise list of what didn't, which is very different from a REST call that returns either a body or an error status.

Why it matters: A GraphQL response can succeed and fail at once. Clients that only check the HTTP status miss errors, and schemas with careless `!` turn one slow service into a blank page.

  1. 1Request errors stop everything

    Syntax and validation problems are found before execution. The response has `errors` and no `data` key.

  2. 2Execution errors are local

    If a resolver throws, that field becomes null, an entry with a `message` and a `path` goes into `errors`, and the other fields still resolve.

  3. 3Nulls climb past `!`

    A non-null field can't hold null, so its error makes the parent null instead, and so on up to the first nullable position.

  4. 4Expected failures can be data

    "Email already taken" is part of your domain. Many schemas return it as a typed result (a union or a payload with `userErrors`) instead of an error.

1

How it works

The explanation, with examples you can run.

The shape of an error

{  "data": { "post": { "title": "Hello", "viewCount": null } },  "errors": [    {      "message": "stats service timed out",      "locations": [{ "line": 4, "column": 5 }],      "path": ["post", "viewCount"],      "extensions": { "code": "UPSTREAM_TIMEOUT" }    }  ]}

path names the response position that failed, including list indexes (["posts", 3, "author"]). locations point into the query document. extensions is free-form; servers put machine-readable codes there. Keep messages in production generic and log the details server-side: stack traces and internal names in errors tell an attacker a lot.

How far a null travels

⌘/Ctrl + Enter
OutputNot run yet

Press Run to see what your code prints, in order.

Runs in this browser, in an isolated frame with no access to this page. console.log, info, warn and error are shown; code runs as a classic script, so use an async function for await.

The error is recorded once, at the field that failed; what changes is how much of the response is lost. With viewCount nullable, only that number is missing. Marking it Int! costs the whole post, and marking post non-null too costs the entire response. Put ! where a value is truly guaranteed, and leave fields that depend on other services nullable.

Errors as data

Errors in errors are for things that went wrong. Outcomes the user can act on, such as a duplicate email, a declined card or a missing permission they could request, are usually better modelled in the schema:

type Mutation { signUp(input: SignUpInput!): SignUpResult! }union SignUpResult = SignUpSuccess | EmailTaken | InvalidInput

The client then handles each case with typed fields instead of parsing messages, and the schema documents every outcome.

2

Check yourself

3 questions. Get them all right to finish this topic.

  1. 1.A response contains both `data` and `errors`. What does that mean?
  2. 2.`viewCount: Int!` on `Post` throws, and `post` is nullable. What is `data.post`?
  3. 3.What HTTP status does a GraphQL response with a resolver error usually have when served as `application/json`?

0 of 3 right so far. Get all three to finish this topic.