Why is a GraphQL request's operationName unsafe as a shared cache key?
answer
- A selector inside a document, nothing more
- Unique within a document, not across clients
- Two teams pick the same obvious name
- The name never changes when the selection does
- Fine in a log, fatal in a key
basics
~20 sBecause operationName only picks which operation to run out of a document that defines several. It is a client-chosen label, not an identity: two unrelated documents may reuse it, and it says nothing about the variables.
solid answer
~50 sThe GraphQL specification gives `operationName` one job — when a document defines more than one operation, it selects which one executes; with a single operation it may be omitted entirely. That makes it a label, not a fingerprint. An edge rule that keys on the path plus `operationName` therefore fails twice. It merges requests that differ only in variables, so a lookup for one release returns another's body. And it merges two different documents that happen to declare the same name, which is legal and common when separate clients each write a `TrackDetail` query with different selections — so one client is served a response missing fields it asked for, or carrying fields it never requested. Names are also stable across deploys by design, so a client that changes its selection keeps the old key and keeps being served the old shape.
code
graphql · 7 linesquery TrackDetail($trackId: ID!) {
track(id: $trackId) { title durationMs }
}
query TrackCredits($trackId: ID!) {
track(id: $trackId) { credits { role artist { name } } }
}go deeper
Know what operationName does at all: it tells the server which operation to run when the document defines more than one, and it can be left out when there is only one. That is its whole job.
Be able to explain why a label chosen by the client cannot identify a response: names repeat across documents, they say nothing about variables, and they do not change when the selection they name changes.
An interviewer expects you to recognise the shortcut in an edge configuration and predict its symptoms — wrong-argument data with a clean origin — then propose an operation identifier plus canonical variables as the replacement.
Own the boundary: edge configuration encodes assumptions about a schema that the schema's owners never review. Decide who signs off on a cache key rule and how a change to an operation is prevented from silently invalidating it.
## What operationName is actually for A GraphQL document may define more than one operation. When it does, the request has to say which one to execute, and that is `operationName`'s entire specified purpose: the server looks up the operation with that name in the document and runs it. If the document defines exactly one operation, the parameter may be omitted, and if it is present but names nothing in the document, the request is in error. So it is a selector *within a document you already have*. It is meaningful only relative to that document. On its own it names nothing. Everything else `operationName` gets used for is convention layered on top: it is what shows up in server metrics and traces, what an allowlist is often expressed in terms of, and what a log line carries so a human can tell one read from another. Those uses are fine, because in every one of them the name is a *label attached to something else that carries the identity*. A cache key has nothing else. ## Why the shortcut is tempting An engineer configuring an edge in front of a GraphQL API sees a query string like `?query=%7B%20release...&operationName=ReleaseDetail&variables=%7B...%7D` and reaches for a reasonable-sounding simplification: the raw document is enormous and formatting-sensitive, so drop it from the key, and keep the one parameter that reads like the name of the thing. It looks like a URL path. It is short, stable and human-readable. Everything about it feels like an identifier. ## The two collisions **Variables vanish.** If the key is path plus name, every request for that operation shares one entry regardless of what it asked for. On a music catalogue this is immediate and spectacular: the first caller to fetch `ReleaseDetail` for `rel_4471` fills the entry, and every subsequent caller asking for any other release is served *Nightjar Sessions* until the entry expires. The symptom is not subtle, but it is easy to misattribute — the origin is serving correct responses to the requests it actually receives, so its logs and metrics are clean, and only misses reach it at all. **Documents collide.** Names are chosen by whoever wrote the document, and the specification only requires them to be unique *within one document*. Two teams independently writing a `TrackDetail` query is not a mistake; it is the obvious name. If the web client's version selects `title`, `durationMs` and `artist { name }` while the television client's version selects `title` and `credits { role artist { name } }`, then under a name-only key whichever one fills the entry first defines what both receive. The second client gets a body that is missing fields its own document requested — which is not a shape any GraphQL client expects, since the response is supposed to mirror the selection it sent. Client-side decoding fails in confusing ways, or worse, silently renders empty state. **And names outlive selections.** An identifier derived from the document changes when the document changes; a name does not. A client that adds a field to its `TrackDetail` query ships with the same name, so it keeps hitting the entry filled by the previous version and keeps receiving the previous shape for as long as that entry lives — a deployment that appears to do nothing until the cache turns over. ## What to key on instead An operation identifier: a hash of the document from a runtime handshake, or a name-plus-version from a build-time manifest. Both change exactly when the operation changes and are identical across clients that ship the same operation, which is precisely what a name is not. The variables still belong in the key alongside it, serialized the same way by every client. Keeping `operationName` in the URL as well is harmless and often useful — it makes edge access logs readable without a manifest lookup. The rule is only that it must never be the *sole* discriminator. ## Say what is specified Specified: that `operationName` selects an operation from a multi-operation document, that it may be omitted for a single-operation document, and that operation names must be unique within a document. Not specified, and not implied anywhere: that a name is unique across clients, stable in meaning over time, or usable as a cache key. Persisted operation identifiers are themselves a widespread convention rather than part of the GraphQL specification — but unlike a name, they are at least constructed to be identities.
- If operationName is a bad key, why do server metrics and allowlists use it so happily?Because in those places the name is a label attached to something that already carries the identity — the parsed document the server is holding, or the manifest entry an allowlist checked. Ambiguity there is a reporting annoyance at worst: two documents sharing a name merge in a dashboard. A cache key has nothing else attached to it, so the same ambiguity becomes wrong data served to a real user.
- What is the symptom you would look for if you suspected a name-only edge key?Callers receiving data for the wrong arguments, with a completely clean origin — correct responses to every request that reached it, no elevated error rate, and a suspiciously high hit rate for an operation whose variables should be spread over many values. Reproduce it by requesting two different ids in quick succession and comparing bodies; if the second returns the first's data, the variables are not in the key.
- Does it help to require every client to prefix its operation names uniquely?It removes the cross-client collision and nothing else. The variables are still missing from the key, and the name still fails to change when the selection does, so a client that ships a new version keeps hitting the old entry. A naming convention is a coordination cost that buys you a fraction of what an identifier derived from the document gives you for free.
saying these in an interview costs you the question
- Calls operationName a unique document identifier
- Thinks operation names are unique across clients
- Assumes a name changes when the selection changes
- Drops variables from the key to shorten the URL
- Says the server rejects duplicate names between clients