skip to content

questions

3

What makes a GraphQL schema invalid, and when is that caught?

level: juniorimportance: must knowfreq 52%

answer

  1. Two validations, two different moments
  2. One is about the schema itself
  3. It runs once, before any client
  4. The failure is no response at all
  5. Referenced types, unions, interfaces, input positions

basics

~20 s

A schema is invalid when its own definitions break the type-system rules: a missing interface field, a union member that is not an object type, an argument typed as an output type. Servers check this once, while building the schema.

solid answer

~50 s

GraphQL validates twice, in two different places. The **schema** is validated against the type-system rules once, when the server turns SDL or code definitions into an executable schema: every referenced type must exist, root operation types must be object types, union members must be object types, an implementing type must declare every interface field, field arguments must be input types and field return types must be output types, and names may not start with `__`. A schema that breaks any of these is not served at all — the server fails to build it and typically dies at startup, so the symptom is a process that will not come up rather than a response with an `errors` array. Validating an incoming *document* against a schema is the separate, per-request pass, and it presumes the schema was already valid.

code

graphql · 14 lines
graphql
interface CaseRecord {
  id: ID!
  filedAt: String!
}

type Exhibit implements CaseRecord {
  id: ID!
}

union Evidence = Exhibit | CaseRecord

type Query {
  filings(filter: Exhibit): [Exhibit!]
}

go deeper

for a junior

Be ready to say that a GraphQL schema is checked against the type-system rules once, when the server builds it, and that failing means the service does not serve at all. Name two or three concrete rules — undefined types, union members, missing interface fields.

for a middle

Explain the mechanics of the split: which pass owns which rules, why the input-versus-output type positions exist, and what a server does with a schema it cannot build. Be able to look at a short SDL file and point at the offending line.

for a senior

Show you treat an invalid schema as an availability event, not a bug report. Talk about validating the schema in the build rather than discovering it on deploy, and about why there is no partial-degradation story when a schema fails to assemble.

for a principal

Own where schema validity is enforced across many teams: a build gate, a pre-publish check, or the runtime as last resort. The tradeoff is how early a bad contract is caught versus how much process sits between an author and a deploy.

## Two validations, two moments Almost every confusing GraphQL error message becomes obvious once you know which of two validation passes produced it. **Schema validation** asks: *are these type definitions internally coherent?* It runs once, when the server assembles an executable schema out of SDL text or out of code definitions, before any client exists. Its input is only the schema. **Document validation** asks: *does this operation make sense against that schema?* It runs per request, and it presumes the schema already passed. This leaf is about the first pass. The practical consequence is the failure mode. A document that breaks a rule comes back as a normal HTTP response carrying an `errors` array. A **schema** that breaks a rule produces no response at all: the server cannot construct the thing that would answer, so it throws while wiring up and the process does not reach a listening state. "The service will not start" and "the service returned an error" are different incidents, and the first is a total outage of every operation at once. ## The rules a server checks The specification's type-system section lists validity rules per definition kind. Working in a legal case-file graph, the ones that bite in practice: **Every referenced type must be defined.** A field typed `Docket` when nothing defines `Docket` is not a runtime lookup failure; it is an invalid schema. **Root operation types must be object types,** and a schema must have a query root. A schema whose mutation root is an interface is invalid. **Union members must be object types.** Not interfaces, not other unions, not scalars, not input objects. `union Evidence = Exhibit | Testimony | CaseRecord` is invalid the moment `CaseRecord` is an interface — a union is an open set of concrete alternatives, and members that are themselves abstract would make the runtime type undecidable. **An object type implementing an interface must declare every field that interface declares,** with compatible types and identical argument types, and must also declare every interface that interface itself implements. **Arguments must be input types; fields must return output types.** Scalars and enums sit on both sides of that split, but an object, interface or union may only be a field's *return* type, and an input object may only be an *argument* or input-field type. Declaring `filings(filter: CaseFile): [Filing!]` where `CaseFile` is an object type is invalid — there is no way to write an object type as a literal or a variable value. **Input object fields must be input types,** for the same reason, and an input object that references itself through a chain of non-null, non-list fields is invalid because no finite value could satisfy it. **Names beginning with `__` are reserved** for introspection, so you cannot define `__caseId`. Enum values may not be spelled `true`, `false` or `null`, because those literals already mean something else in a document. ## Why validate once instead of per request Three reasons. Cost: these checks are quadratic-ish over the type graph and would be wasted work on every request. Determinism: an invalid schema is a deployment defect, not a client mistake, and failing loudly at build or startup puts the error in front of the person who caused it. And integrity of everything downstream: introspection results, typed client generators, registries and any tooling that reads the schema all assume a schema that already passed — serving a half-coherent schema would corrupt every consumer rather than one caller. ## Where you find out When the schema is a checked-in SDL artefact, a build step can validate it and fail the pipeline. When the schema is assembled from code definitions at process start, the same rules run then, and you learn on deploy. Either way the fix is the same: get schema validation into continuous integration so an invalid schema never reaches a running environment, because there is no partial degradation available — a schema either builds or the service does not exist. ## What it does not check Schema validation is purely structural. It has no opinion on whether your names are good, whether a change you just made will break an existing client, whether a field is expensive to resolve, or whether a resolver exists for every field. A schema can be perfectly valid and still be a bad contract, and a valid schema can still return errors on every request.

  • If the schema is invalid, what does a client actually observe?
    Nothing GraphQL-shaped. The server never finishes building the executable schema, so it does not begin serving — callers see a connection refused, a health-check failure, or whatever the platform does with a process that exits during startup. There is no `errors` array, because producing one would require the very schema that failed to build.
  • Can a schema be valid and still be missing a resolver for a field?
    Yes. Schema validity is a property of the type definitions alone. Whether every field has an implementation is a server concern, and servers differ: some refuse to build unless each field is either mapped or resolvable by the default property lookup, others happily serve a field that always returns null. That check, where it exists, is a server policy layered on top of the specification's rules.
  • Why are names starting with a double underscore rejected?
    That prefix is reserved for the introspection system — `__schema`, `__type`, `__typename` and the meta-types behind them. Reserving the whole prefix keeps introspection meta-fields unambiguous forever, so a schema author can never define a field whose name would collide with a meta-field the execution engine injects.

Schema validation is the building inspection before the doors open; document validation is the ticket check at the door. Failing the inspection means nobody gets in at all, and no ticket is even examined.

saying these in an interview costs you the question

  • Thinks an invalid schema returns an errors array
  • Confuses schema validation with validating an incoming document
  • Believes a union may list an interface as a member
  • Says the schema is re-validated on every request
  • Assumes an object type may be a field argument's type
  • Claims a valid schema cannot break clients

context

open as a page

What must an object type do to validly implement an interface?

level: middleimportance: should knowfreq 54%

basics

~20 s

Declare every field the interface declares, returning the same type or a narrower subtype, and repeat every interface argument at exactly the same type. Extra arguments must not be required, and transitively implemented interfaces must also be listed.

open as a page

Why can a recursive input object make a schema fail validation?

level: seniorimportance: nice to knowfreq 22%

basics

~20 s

A chain of Non-Null fields that returns to the same input object can never be satisfied by a finite value. The specification requires at least one link in such a cycle to be nullable or a list type.

open as a page