What is the top-level extensions key in a GraphQL response for, and what may a client assume about it?
answer
- The response map is otherwise closed
- Reserved for implementors, no rules on contents
- Unrestricted means unportable
- Never let a client depend on it
- Failures still belong in the errors entry
basics
~20 sIt is the one sanctioned place for anything a server sends beyond data and errors, because no other top-level entry is allowed. It must be a map when present, and a client may assume nothing about its contents.
solid answer
~40 sThe top-level response map is closed — `data`, `errors` and nothing else — so `extensions` exists as the single hole in that closure. It is optional; when present its value must be a map; and the specification places **no restrictions at all** on what that map contains, because it is reserved for implementors to extend the protocol as they see fit. That freedom is also the catch: because nothing is specified, nothing is portable. A client written against one server's `extensions` contents is written against that server, and a client that needs a value from `extensions` to render correctly has made a vendor-specific dependency load-bearing. Treat it as strictly additive diagnostics — a request correlation id, a schema version — and never as a channel for reporting failures, which belong in `errors`.
code
json · 5 lines{
"data": { "release": { "title": "Nightshift Sessions" } },
"requestId": "c1f47b92",
"schemaVersion": "2026.31"
}go deeper
Just recognise the key: it is optional, it is a map, and it holds whatever the server chose to attach. You are not expected to have used it, only to not be surprised when a response body contains one.
Explain that the top-level map is closed and that extensions is the single reserved slot for anything else, and that its contents carry no specified meaning. Be able to say why a client should not branch on what it finds there.
Show the judgement: put diagnostics there rather than inventing a top-level key, keep every consumer able to work without it, and keep failures in the errors entry. Distinguish it cleanly from a per-error extensions map and from the request-side slot.
Own the coupling question. A house convention inside extensions is unspecified, unversioned and invisible to introspection, so decide deliberately which metadata is worth that debt and which belongs in the schema where it can be typed, deprecated and evolved.
## The one hole in a closed map The response envelope is deliberately closed: `data`, `errors`, `extensions`, and the specification says the top-level map must not contain any other entry. That closure is what makes the protocol safe to evolve — a future edition can define a new top-level entry knowing no conformant server has already claimed the name for something else. But servers genuinely do have things to say about a response that are neither data nor errors. `extensions` is the sanctioned answer: one named slot, explicitly reserved for implementors, into which anything else goes. Its rules are almost comically short. It is **optional** — most responses omit it. When present, its value **must be a map**, not a list and not a string. And there are **no additional restrictions on its contents**. That is the entire contract. ## Why "no restrictions" cuts both ways The freedom is the feature: a server can attach anything without breaking a conformant client, because a client that does not recognise the key simply ignores it. The freedom is also the trap: **nothing in `extensions` is specified, so nothing in it is portable.** Two servers built on different stacks will not agree on the keys inside it, and there is no schema, no introspection and no version negotiation covering it. A client that reads `extensions.someKey` and behaves differently based on what it finds has coupled itself to one particular server implementation through a channel with no contract and no deprecation process. The practical rule that falls out: `extensions` may inform, but it must never be load-bearing. If a client cannot render a correct result without reading `extensions`, the design is wrong and whatever it needs belongs in the schema, where it is typed, introspectable and versioned like everything else. ## A worked decision A four-person platform team owns a music catalogue graph consumed by three internal applications. Support tickets are hard to trace, so they want every response to carry a correlation id and the schema version it was produced against. The tempting move is a new top-level key: ```json { "data": { "release": { "title": "Nightshift Sessions" } }, "requestId": "c1f47b92", "schemaVersion": "2026.31" } ``` That body is non-conformant. Strict clients and validators are entitled to reject it, and the team has quietly claimed two names in a namespace they do not own. The conformant version puts exactly the same information one level down: ```json { "data": { "release": { "title": "Nightshift Sessions" } }, "extensions": { "requestId": "c1f47b92", "schemaVersion": "2026.31" } } ``` Now the information travels, no conformant client breaks, and — crucially — the team's own tooling is the only consumer. Their support workflow reads `extensions.requestId`; the three applications ignore the key entirely and would still work if it vanished tomorrow. That is the correct dependency shape. ## What does not belong there **Failures.** If something went wrong, it goes in `errors`. A server that reports a problem only through `extensions` has made itself invisible to every generic client, every logging middleware and every alert that watches for the `errors` entry — and there is no reason to, because `errors` is right there and standard. **Anything the caller must read.** Covered above, but it is the mistake worth naming twice in an interview. **Anything you would not show every caller.** The same response body reaches whoever asked, so treat the slot as public output rather than as an internal debugging channel that happens to ride along. ## Two adjacent slots not to confuse it with There is a **second `extensions` in the response**: each individual entry in the `errors` list may carry its own `extensions` map, describing that one error. Same name, different scope — the top-level one is about the response as a whole. There is also an `extensions` slot on the **request** side. The GraphQL over HTTP specification defines the request body's parameters, and alongside the document, operation name and variables it allows an `extensions` map, which ecosystem conventions use to carry per-request metadata to the server. Again the same name, again a different direction of travel, and again unrestricted contents. Being able to say which of the three you mean, unprompted, is most of what a senior answer to this question looks like. ## Why it is a nice-to-know Nobody is turned down for not knowing this. It is a small, well-defined corner of the envelope that most engineers never touch, because the ecosystem conventions that use it are handled inside client libraries. But an interviewer who asks it learns quickly whether the candidate reasons about protocol namespaces and vendor coupling, or reaches straight for a new top-level key.
- Why does the specification close the top-level response map instead of simply letting servers add keys?For forward compatibility. If servers claimed arbitrary top-level names, a future edition of the specification could not add an entry without colliding with something already deployed. Closing the map and funnelling every vendor addition into one reserved slot keeps the top level as a namespace the specification alone controls, while still letting implementations carry whatever they need.
- Is there an extensions slot on the request side as well?Yes, and it is a different thing with the same name. The GraphQL over HTTP specification defines the request body's parameters, and alongside the document, operation name and variables it allows an optional `extensions` map for per-request metadata travelling to the server. Its contents are likewise unrestricted and set by ecosystem convention rather than by the core specification.
- Should a server ever report a failure only through the top-level extensions map?No. Anything that is an error belongs in the `errors` entry, which is the standard channel every generic client, proxy and logging layer already inspects. Reporting through `extensions` instead makes the failure invisible to all of them, and there is nothing `extensions` offers that `errors` does not, since each error entry has its own extensions map for structured detail.
It is the blank 'anything else?' box at the bottom of a fixed form: you may write in it, but nobody who reads the form is obliged to read your handwriting.
saying these in an interview costs you the question
- Adds a custom top-level key beside data
- Says extensions is required on every response
- Depends on another server's extensions contents
- Reports failures through extensions instead of errors
- Thinks extensions may be a list or a string
- Confuses it with a per-error extensions map