skip to content

questions

3

What does GraphQL validation check between parsing a document and executing it?

level: juniorimportance: must knowfreq 68%

answer

  1. Runs before the first resolver
  2. Two inputs only, nothing else
  3. The schema is the whole context
  4. Fields, arguments, literals, fragments
  5. Errors present, data key absent

basics

~20 s

Validation compares the parsed document against the schema alone: each selected field must exist on its type, each required argument must be supplied, and each fragment must be used and acyclic. A document that fails runs no resolvers.

solid answer

~50 s

Between parsing and execution a GraphQL server runs a validation pass whose inputs are exactly two: the parsed document and the schema. No resolver runs, no backend is touched, and the values behind variables are not consulted. The rules are the ones decidable from those two inputs — a field selected on a type must be declared on that type, an argument must be one the field declares, an argument that is Non-Null with no default must be supplied, a literal must fit its input type, every fragment defined must be spread somewhere, and fragment spreads must not form a cycle. The pass is all-or-nothing: one broken rule fails the request before the first resolver, and the response carries `errors` with no `data` key present. Because the schema is the only context needed, the identical rules can run in an editor or a build long before a request exists.

code

graphql · 16 lines
graphql
query Payslips($runId: ID!) {
  payRun(id: $runId) {
    reference
    payslips {
      netPayCents
      employee {
        fullName
      }
    }
  }
}

fragment TaxLines on Payslip {
  taxCode
  amountCents
}

go deeper

for a junior

Be ready to name the three phases in order and say plainly that validation compares the document to the schema before any resolver runs. Knowing that an invalid document executes nothing is the part interviewers actually listen for.

for a middle

Expect to enumerate the rule families — field exists on its type, arguments declared and required ones supplied, literals fitting their input types, fragments used and acyclic — and to explain why each is decidable from the schema alone.

for a senior

Demonstrate that you know where the boundary sits in production: which client failures are validation failures, why they arrive with no data key, and why a green validation pass tells you nothing about whether the request will succeed.

for a principal

Own the argument that a checkable contract is the point of the schema. Be able to say what an organization gains by moving failures from runtime to a static pass shared by editors, builds and servers, and what it costs when the schema changes underneath deployed clients.

## Where the pass sits, and what it is allowed to look at A GraphQL request moves through three phases. The request text is **parsed** into a document — a tree of operation definitions, selection sets, arguments, variable definitions and fragment definitions. That document is then **validated** against the schema. Only if validation finds nothing wrong does **execution** begin and the first resolver run. Validation's defining property is the narrowness of its inputs. It is handed the parsed document and the schema, and nothing else. It does not see the variable values that arrived with the request, the caller's identity, a database, or a single line of resolver code. That is what makes it a *static* pass, and it is why the very same rules can run inside an editor as you type or in a build step, long before the request exists — the schema is available offline, and the schema is all the pass needs. ## The rules, grouped by what they compare The specification enumerates the rules in detail, but they fall into four families that are easy to hold in your head. Take a payroll and benefits graph where `PayRun.payslips` returns `[Payslip!]!` and `Payslip` declares 37 fields. **Does each selected field exist on the type it is selected on?** Selecting `netPayCents` on `Payslip` is fine; selecting `netPayCent` is a validation error, because the rule is a lookup in the schema rather than a guess about intent. The same family decides whether a field returning an object was given a selection set at all, and whether a field returning a scalar was wrongly given one. **Are the arguments ones the field declares, and are the required ones present?** An argument name the field does not declare fails. An argument whose type is Non-Null and carries no default is *required*: omit it and the document is invalid before anything executes. An argument that is nullable, or that has a default, may be omitted freely — the field simply receives no explicit value. **Do the literal values written in the document fit their input types?** A string literal where the schema wants `Int`, an enum name the enum does not define, an input object missing a required field — all rejected. This is a shape check against the type system, not a meaning check. Note the boundary carefully: literals are written *in the document*, so validation can see them; the values behind variables travel with the request and are handled by coercion, which is a separate step. **Is the document's own fragment structure coherent?** Every fragment definition must be spread at least once, a spread must name a fragment that exists, and the spreads must not form a cycle. Further rules share the same character: operation names must be unique within the document, a directive must be one the schema defines and must appear in a location that directive allows, every variable used must be defined and every variable defined must be used. In each case a schema plus a document is enough to decide the question. ## One broken rule fails everything Validation is all-or-nothing. If any rule is violated the request fails **before execution begins** — no resolver is called, no backend is touched, and the response carries an `errors` list with no `data` key present at all. This is what makes a validation failure a *request* error rather than a *field* error: a field error happens during execution and leaves partial data behind it, while a validation failure leaves nothing to be partial about. Implementations conventionally report every rule violation they find rather than stopping at the first, so one round trip tells you everything wrong with the document. That is a convention, not a specified requirement — what the specification insists on is that an invalid request must not execute. ## What the pass deliberately cannot decide Because it sees only two inputs, validation cannot know whether employee `E-4182` exists, whether the caller is allowed to read salary fields, whether a list will return nine rows or four hundred thousand, or whether a dependency is healthy. Every one of those belongs somewhere else — to execution, or to a control the specification does not define. Reading a clean validation pass as "this request will succeed" is the single most common misunderstanding of what the pass is for. ## Why having it at all is the point In a conventional HTTP API a misspelled query parameter is usually ignored: the server has no declared vocabulary to check it against, so the typo ships and shows up as missing behaviour weeks later. In GraphQL the same typo is a hard, early, machine-readable failure that names the field and points at a location in the document — precisely because both ends hold the same schema. Validation is where that shared schema is cashed in.

  • If the rules need only the schema and the document, why does the server run them on every request?
    Because the server cannot trust that anyone ran them. Documents come from deployed clients that may be months old, from scripts, and from anonymous callers, and the schema moves under all of them. The specification does allow a server to skip re-validating a document it already knows to be valid against the current schema, which is why servers keep validated documents around rather than repeating the work per request.
  • Does a server report the first rule violation it finds, or all of them?
    The specification only requires that an invalid request must not execute; it does not say how many errors to list. In practice implementations collect every violation and return them together, so one round trip tells the client everything wrong with the document. Expect a single mistake — a renamed type, say — to produce many entries, one per selection that referenced it.
  • Does a document that passes validation always execute successfully?
    No. Validation proves the document is a legal request against this schema, nothing more. A resolver can still throw, a record can be missing, the caller can be unauthorized, a dependency can time out. Those surface as field errors during execution, with partial data and an errors list, which looks nothing like a validation failure.

Validation is the spell-check a shared dictionary makes possible: it can tell you that netPayCent is not a word in this schema's language, but it can never tell you whether the sentence you wrote is true.

saying these in an interview costs you the question

  • Thinks validation calls resolvers to check fields
  • Says validation confirms the requested data exists
  • Believes an unknown field is silently dropped
  • Expects partial data alongside a validation failure
  • Assumes variable values are checked by validation rules
  • Treats a clean validation pass as a success guarantee

context

open as a page

A GraphQL document validates cleanly and still returns 388,514 rows — why didn't validation stop it?

level: seniorimportance: should knowfreq 46%

basics

~20 s

Validation compares a document only to the schema. A list field whose paging argument is optional is a legal selection however many rows come back, so nothing static can object. The bound must come from the schema and a resolver clamp.

open as a page

Why must fragment spreads in a GraphQL document never form a cycle?

level: middleimportance: nice to knowfreq 24%

basics

~20 s

Spreading a fragment inlines its selections rather than calling it, so two fragments that spread each other denote a selection set with no end. The specification makes cycles a validation error, so such a document fails before execution.

open as a page