skip to content

questions

4

What does a GraphQL POST request body contain, and what is each key for?

level: juniorimportance: must knowfreq 74%

answer

  1. One JSON object, one URL
  2. Four parameters, not one
  3. The document is only one of them
  4. Which operation, and with what values
  5. query, variables, operationName, extensions

basics

~20 s

A GraphQL POST body is a JSON object with up to four keys: query, the operation document text; variables, a map of variable values; operationName, naming which operation in the document to run; and extensions, an implementation-defined map.

solid answer

~50 s

The ordinary GraphQL HTTP request is a JSON object POSTed to one URL. `query` holds the executable document as a string — the whole document, which may declare several operations and any fragments they use. `variables` is a map from variable name to value, matching the variable definitions the operation declares; sending values here rather than splicing them into the document text keeps the document constant and lets the server coerce and validate each value against its declared type. `operationName` selects which operation to execute; it may be omitted when the document declares exactly one, and is required when it declares more than one. `extensions` is a map reserved for implementers to carry anything the specification does not define, and a server may ignore entries it does not recognise. Note the request `extensions` key is a different slot from the `extensions` entry a response may carry back.

code

json · 6 lines
json
{
  "query": "query MenuBoard($restaurantId: ID!) {\n  restaurant(id: $restaurantId) {\n    name\n    menu { items { id name priceCents } }\n  }\n}",
  "operationName": "MenuBoard",
  "variables": { "restaurantId": "r-4417" },
  "extensions": { "kioskBuild": "3.14" }
}

go deeper

for a junior

Be able to write the body by hand from memory: a JSON object with query, variables, operationName and extensions, POSTed to one URL. Knowing that variables is a nested object and not a string is the detail that gets checked.

for a middle

Explain why the parameters are split the way they are — constant document, typed and coerced variables, explicit operation selection — and state the exact rule for when operationName is required and what happens when it is missing.

for a senior

Show you know which half comes from the GraphQL specification and which from the GraphQL over HTTP working draft, and be ready to say what the envelope does not carry: no credentials, no routing information, nothing an intermediary can read without parsing the body.

for a principal

Own the client-side contract this shape implies across teams: whether documents are assembled at build time, whether operationName is mandated for observability, and what your organisation is allowed to put in extensions given nothing about it is portable.

## Two layers, two documents The GraphQL specification describes a request abstractly: a document, a set of variable values, and optionally the name of the operation to run. It never says HTTP. The rules for expressing that request as an HTTP request live in a separate document, the GraphQL over HTTP specification, which is still a working draft and which mostly writes down what implementations had already converged on. So when someone asks "what is in a GraphQL request body", the honest answer names four parameters and says where each comes from. ## The four parameters **`query`** — a string containing the *executable document*. The name is a historical wart: this key carries mutations and subscriptions too, not just query operations. The document is the whole text, so a request that sends three named operations and four fragments still sends them all in this one string. The server parses it, validates it against the schema, and only then executes. **`variables`** — a map from variable name (without the `$`) to value. Over a JSON POST it is a nested JSON object, not a string. Variables exist so the document text can be a constant: the same document with different values parses to the same AST, hashes to the same key, and validates once. They are also typed — each is declared in the operation's variable definitions (`query MenuBoard($restaurantId: ID!)`), so the server coerces the incoming JSON value to the declared input type and raises a request error before execution if it cannot. Building the document by string concatenation instead loses all of that. **`operationName`** — a string naming which operation in the document to execute. The core specification's operation-selection rule is precise: with no `operationName` and exactly one operation in the document, that operation runs; with no `operationName` and more than one operation, the request is in error and nothing executes; with an `operationName` that matches no operation in the document, likewise. Clients that ship one large document containing many operations therefore must send it on every request. It is also the single most useful label the request carries — the only human-readable identifier of what this POST is doing. **`extensions`** — a map reserved for implementers. The specification defines the slot and deliberately says nothing about its contents, which is what makes it the standard place to put protocol additions that are not part of GraphQL itself. Anything a server puts there is that server's contract with its own clients, not a GraphQL guarantee, and a server that meets an entry it does not understand is free to ignore it. ## What is *not* in the body Authentication is not a GraphQL parameter. Credentials ride in HTTP headers or cookies as they would for any other endpoint, and the schema never sees them unless the server threads them into its execution context. Nor is there a per-operation URL: the same URL takes every operation, and the path carries no routing information about what is inside. That is a deliberate part of the design and it has consequences — for anything that wants to classify traffic without parsing a JSON body, this shape is opaque. ## A worked request For a restaurant ordering graph, a kiosk fetching a menu board sends a JSON object whose `query` is the document text, whose `variables` supplies `restaurantId: "r-4417"`, whose `operationName` is `MenuBoard`, and whose `extensions` might carry the kiosk build identifier so the server can attribute traffic. Every one of those pieces is separately parseable by the server before a single resolver runs. ## Common mistakes worth naming Sending `variables` as a *string* containing JSON inside a JSON body is a frequent bug — that encoding belongs to the URL-parameter form of a request, where everything must be a string, not to a JSON body where it is a nested object. Omitting `operationName` for a multi-operation document produces a confusing failure: the server rejects the request outright rather than guessing, and the response carries no data at all rather than a partial result. And treating `extensions` as specified behaviour — assuming another server will understand the entries yours emits — is a portability trap; it is an extension point precisely because nothing about it is agreed. ## Why interviewers ask it Because almost everyone has used a client library that assembles this body for them and never looked at it. Being able to describe the envelope by hand — and to say which parts come from the GraphQL specification and which from the serving draft — separates a candidate who has debugged a graph from one who has only called one.

  • The document declares two named operations and the request omits operationName. What does the server do?
    Nothing executes. Operation selection happens before execution, so a document with more than one operation and no `operationName` is a request error: the server cannot choose, does not guess, and answers with an error and no data at all rather than a partial result. The same applies when the supplied `operationName` matches no operation in the document.
  • Why send values in variables rather than interpolating them into the document string?
    Three reasons. The document text stays constant, so parsing and validation results can be reused and the document has a stable identity. Each value is coerced and checked against the variable's declared input type before execution, so a bad value fails cleanly. And values keep their JSON types — numbers, booleans, nulls, nested input objects — instead of being flattened into text that must be re-quoted correctly.
  • Is a GraphQL request body required to be JSON?
    Not by the GraphQL specification, which is serialization-agnostic and merely recommends JSON while defining how its map, list and null concepts map onto a format. In practice the GraphQL over HTTP draft standardises a JSON POST body, that is what servers accept, and treating anything else as portable is a mistake.

The body is a job ticket: the document is the whole instruction booklet, operationName says which page to carry out, variables fill in the blanks on that page, and extensions is the margin where the shop writes its own notes.

saying these in an interview costs you the question

  • Thinks variable values must be inlined into the document string
  • Calls operationName the name of the field being fetched
  • Believes each operation needs its own HTTP endpoint
  • Sends variables as a JSON-encoded string inside a JSON body
  • Assumes extensions has specification-defined contents
  • Says the query key only carries query operations

context

open as a page

How is a GraphQL operation encoded in a GET request, and which operations may travel that way?

level: middleimportance: should knowfreq 47%

basics

~20 s

Over GET the parameters move into the URL query string: query as the percent-encoded document, operationName as a plain string, and variables and extensions as JSON serialized to a string and then percent-encoded. GET carries query operations only.

open as a page

A GraphQL endpoint answers every read and write as a POST to one URL. How do you stop an automatic retry duplicating a write?

level: seniorimportance: should knowfreq 41%

basics

~20 s

Nothing outside a GraphQL request body distinguishes a read from a write — same URL, same method, same headers — so no intermediary can retry safely. Move retry decisions to the code that authored the operation, or make the write repeat-safe.

open as a page

Does the GraphQL specification define how a request travels over HTTP?

level: middleimportance: nice to knowfreq 27%

basics

~20 s

No. The GraphQL specification is transport-agnostic: it defines the language, the type system and the execution algorithm, and stops at a result. HTTP serving is described by a separate document, GraphQL over HTTP, still a working draft.

open as a page