What does a GraphQL server store in a parsed-document cache, and what is the cache key?
answer
- Some request work repeats identically every time
- Two of the three phases are pure
- Depends only on text plus schema
- Variables arrive later, so they are excluded
- Reloading the schema invalidates the verdict
basics
~20 sIt stores the parsed syntax tree for a document plus the verdict that it validated cleanly against the current schema, keyed by the exact document text or a hash of it. Variables are never part of the key.
solid answer
~50 sTurning request text into a result has three phases: parse the **document** into a syntax tree, validate that tree against the schema, then execute it. Parse and validate depend on only two inputs - the document text and the schema - so a server can do them once and keep the result under a key derived from that text, usually a hash of it rather than the full string. Execution is not cached that way, because it also depends on the variable values, the caller and live backend data. Nothing here is in the GraphQL specification, which describes the phases but says nothing about reusing them; it is a near-universal server implementation technique. The schema is the hidden second input: a server that reloads its schema has to discard or re-scope the entries, or a document validated against the old schema keeps a stale verdict.
code
json · 9 lines{
"query": "query Listing($id: ID!) { listing(id: $id) { price status } }",
"variables": { "id": "L-30418" }
}
{
"query": "query Listing($id: ID!) { listing(id: $id) { price status } }",
"variables": { "id": "L-77129" }
}go deeper
Be ready to name the three phases in order and say which two a server can reuse. Remember that the key comes from the document text, and that variables travel separately so they never affect it.
Explain why the key is a hash of the exact bytes rather than a normalised form, and why the schema has to be part of the key. Be able to say plainly that this is a server technique, not a specified rule.
An interviewer expects you to treat the hit ratio as an operational signal and to know what invalidates entries: a schema reload, a redeploy, a client that stops sending stable text. Say what you would alert on.
Own the argument that document stability is a contract with client teams, not a server tuning knob. Decide whether you accept arbitrary text at the edge at all, and what that choice costs in client tooling and deploy coordination.
## Why anything is cached at all Every GraphQL request carries an executable **document**: the operation text a client sends in the request body. Before a single field resolves, a server has to turn that text into something it can run. It **parses** the text into a syntax tree, then **validates** that tree against the schema - are these fields declared on these types, are the fragments spread on compatible types, are all used variables declared, is the operation selected by `operationName` present, and so on. Only then does it execute. The important observation is what each phase depends on: - **Parse** depends on the document text alone. - **Validate** depends on the document text and the schema. - **Execute** depends on those *plus* the variable values, the caller's identity, and whatever the backends currently hold. The first two are pure functions of inputs that do not change between two requests carrying the same text against the same schema. So a server computes them once and holds on to the answer. Do not call this a specified behaviour: the GraphQL specification defines the phases and their ordering, not any reuse of them. Every serious server implementation caches anyway, and an interviewer treats it as expected engineering rather than a rule you can cite. ## What is stored The cached value is the **parsed document** - the syntax tree - together with the result of validating it: normally just "clean", and in some implementations the list of validation errors so a repeated bad document is rejected without re-walking it. That is all. No data, no response, no resolver output. ## What the key is The key is the **document text**, byte for byte, or more commonly a fixed-width hash of it so the cache does not retain a second copy of every large document as its own key. Byte-exactness has a consequence worth saying out loud in an interview: two documents that differ only in whitespace, in the order of their fields, or in the order of their fragment definitions are *different keys*, even though they mean the same thing. Servers do not normally normalise or canonicalise before keying, because normalisation costs roughly what parsing costs and would defeat the point. This is one reason generated or build-time-extracted operation text caches so well - the same bytes come back every time - while hand-assembled text does not. ## What is not in the key **Variables are not in the key**, and that is the entire payoff. In a real-estate listings graph, a document that reads `query Listing($id: ID!) { listing(id: $id) { price status } }` is one cache entry no matter whether the client asks for listing `L-30418` or `L-77129`, because the id travels in the separate variables map at execution time. One entry serves every value the operation is ever called with. Also not in the key: the caller, the headers, the HTTP method. None of them change whether the text parses or whether it is valid against the schema. ## The schema is the second input, and it is easy to forget Because validation is validation *against a schema*, a cached "this document is valid" verdict is only true for the schema it was checked against. A server that hot-reloads or re-composes its schema at runtime must either key entries by a schema generation number or throw the whole cache away on reload. Skip that and a document referencing a field you just removed keeps its stale clean verdict, sails past validation, and fails messily during execution instead of being rejected as a request error before anything ran. ## Where it lives, and what it is worth It is normally an in-process, per-instance cache in front of the executor - not shared between instances, not durable. A freshly started instance is cold and warms within seconds under any real traffic, so the cold start is rarely worth engineering around. The saving scales with document size. A three-field document is cheap to parse and validate either way. A large generated document with several fragments and forty-odd selections is not: validation walks the document once per rule, and doing that on every request at a busy endpoint is measurable CPU that produces the same answer every time. The number to watch is the **hit ratio**. A client population that sends stable text should sit near 100%. A ratio that sags means someone is minting fresh document text per request - which is the next question on this leaf. ## Relationship to sending an identifier instead of text When a client sends a hash or a registered identifier instead of the text, the stability this cache wants is guaranteed by construction rather than hoped for: identical bytes every time, and a small fixed key. That is a separate mechanism with its own tradeoffs, but it is why the two ideas are always discussed in the same breath.
- Why can the parse and validation result be reused across requests, but not the execution result?Parsing and validating are functions of the document text and the schema only, both of which are identical across the two requests. Execution additionally consumes the variable values, the caller's identity and permissions, and whatever the backing stores currently hold - all of which differ per request. Reusing an execution result would mean serving one caller's data to another, or serving stale data; that is a different mechanism entirely, with its own keying and freshness rules.
- Two clients send the same operation but format it differently. Do they share a cache entry?No. The key is the document text byte for byte, or a hash of those bytes, so a difference in indentation, line breaks or the order of fields produces a different key and a second entry holding an equivalent tree. Servers do not usually canonicalise first, because normalising costs about what parsing costs. It is a practical argument for clients sending text produced by a build step rather than assembled by hand.
- What breaks if a server reloads its schema and keeps the cache?The stored verdict says a document is valid, but it was valid against the previous schema. A document selecting a field that the reload removed passes validation from the cache and then fails during execution, surfacing as a field error or an internal error rather than a clean request error before anything ran. The fix is to include a schema generation in the key, or clear the cache on every schema swap.
It is the difference between compiling a program and running it: the compile output depends only on the source and the language, so it is kept, while each run's output depends on that run's arguments.
saying these in an interview costs you the question
- Claims the GraphQL specification requires caching parsed documents
- Thinks the variables map is part of the cache key
- Believes the cache stores the response data
- Forgets validation depends on the schema, not just the text
- Assumes whitespace differences still hit the same entry
- Confuses this with a client's normalized entity cache