How is a GraphQL operation encoded in a GET request, and which operations may travel that way?
answer
- Same parameters, different container
- A query string has no types
- Maps need two encodings, not one
- The method's meaning constrains the operation type
- Only query operations ride here
basics
~20 sOver 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.
solid answer
~50 sA GET request puts the same four parameters into the URL query string instead of a JSON body. Because a query string is flat name-and-value text, the two map-valued parameters change shape: `variables` and `extensions` must first be serialized to JSON and then percent-encoded, so they arrive as strings the server parses back into maps — unlike a POST body, where they are nested objects. `query` and `operationName` are ordinary strings and are percent-encoded as-is. The important restriction is on the operation type: GET is reserved for `query` operations, and a server that receives a mutation over GET must not execute it and refuses the request instead. Two practical limits follow. Documents are bounded by the URL length that browsers, proxies and servers accept, so GET suits short documents; and everything in the request is now visible in URLs, which is where logs and referrers capture it.
code
graphql · 6 linesquery MenuBoard($restaurantId: ID!) {
restaurant(id: $restaurantId) {
name
menu { items { id name priceCents } }
}
}go deeper
Know that a GraphQL operation can travel in a URL as well as in a POST body, and that the parameter names are the same four either way. You are not expected to hand-encode one, only to recognise it.
Explain the encoding precisely: percent-encoded document and operation name, and JSON-then-percent-encoded maps for variables and extensions. State the restriction to query operations and why the method's meaning is what forces it.
Judge when GET is worth the trouble on a real service — document length against proxy limits, what ends up in logs and history, and the client-side complexity of choosing a method per operation.
Decide the organisation's default carriage and make it uniform: which operations may use GET, what may never appear in a URL, and how client teams are prevented from drifting into per-team encoding choices.
## The same parameters, a flatter container The GraphQL over HTTP working draft describes one request with two carriers. A POST puts `query`, `variables`, `operationName` and `extensions` into a JSON body. A GET puts the same four into the URL's query string. Nothing about execution changes: the server ends up with the same document, the same variable map and the same operation selection either way. What does change is the encoding, and the reason is that a query string has no types. It is a flat sequence of `name=value` pairs of text. `query` and `operationName` are already strings, so they are simply percent-encoded — braces, spaces, `$`, `:` and parentheses all become escapes, which is why a hand-written GraphQL URL looks like line noise. `variables` and `extensions` are maps, and a map cannot be expressed in that grammar. So the rule is two-step: serialize the map to JSON, then percent-encode the resulting JSON text as the parameter value. The server reverses it — percent-decode, then JSON-parse — and coerces each variable against the operation's declared type exactly as it would from a POST body. This is the single most common GET mistake in practice. A client that passes the variables object straight through, or spreads it into repeated `variables[restaurantId]=r-4417` pairs, produces a request the server cannot read, and the failure looks like a variable-coercion error rather than an encoding error. ## Why GET is limited to query operations A GET is a read. Nothing in an HTTP GET's contract permits it to change state, and every layer between a client and a server is built on that assumption: a link can be followed, a request can be repeated, a tool can prefetch. GraphQL puts the operation type inside the request body or the `query` parameter, where none of those layers can see it, so the serving rules close the gap by restricting the method: GET carries `query` operations. A server that parses a GET and finds a mutation must not execute it and answers with a refusal instead of a result. That restriction is worth stating in an interview as a *serving* rule rather than a language rule. Nothing in the GraphQL language forbids a mutation document from being sent any particular way; it is the HTTP layer that draws the line, because the meaning of the method belongs to HTTP. ## What GET buys and what it costs The attraction of GET is that a read stops being opaque. The method itself now says "this is a read", which matters to everything that inspects traffic without parsing bodies. The costs are real and worth naming precisely. **Length.** The document, the variables and the operation name all have to fit in a URL. Browsers, reverse proxies and server request-line limits impose ceilings — commonly a few kilobytes — and they are enforced before your code sees the request, so an overlong document fails at the edge with an HTTP-level error and no GraphQL response shape at all. In a restaurant ordering graph, `MenuBoard` with one variable fits comfortably; a deeply nested order-history document with fragments will not. **Exposure.** URLs are logged by every hop, kept in browser history, and can leak through referrers. A POST body is not treated that way by default. If a variable carries anything sensitive — a customer identifier, a token — moving it into a URL moves it into everyone's logs. **Uniformity.** Client code that must decide per operation whether to use GET or POST is more code and one more thing to get wrong, which is why many teams simply POST everything and accept the opacity. ## The shape of a real request For a query named `MenuBoard` taking `$restaurantId: ID!`, the GET is a single URL with three parameters: the escaped document, `operationName=MenuBoard`, and `variables` holding the percent-encoded text `{"restaurantId":"r-4417"}`. The document is unchanged from what would have been POSTed — same text, same fragments, same variable definitions. ## Interview traps Candidates often say "GraphQL only works over POST". It is a reasonable description of what most clients do by default, but it is not true of the serving rules, and the follow-up — *how would you send a query over GET* — is exactly what the interviewer is waiting for. The second trap is claiming that a mutation over GET is fine if the server is willing; the whole point of the restriction is that willingness is not the issue, the method's meaning to intermediaries is.
- Why must variables be JSON-encoded before percent-encoding on GET, when a POST sends it as an object?Because a URL query string is untyped flat text: every parameter value is a string, so a map has no native representation there. The convention is to serialize the map to JSON, then percent-encode that text as the parameter value. A JSON body already has objects, numbers, booleans and nulls, so no intermediate serialization is needed and passing a JSON string there instead is a bug.
- A server receives a GET whose document contains a mutation. What should it do?Refuse the request without executing anything. The operation is selected and its type known before execution begins, so the server can reject it at that point. Executing it would let any layer that treats GET as safe — a prefetcher, a link, a retry — trigger a write, which is precisely what the restriction exists to prevent.
- What bounds how large a GraphQL GET request can be?The URL length that the whole path tolerates: browser limits, reverse-proxy and load-balancer configuration, and the server's own request-line and header size ceilings, commonly a few kilobytes in total. The rejection happens at the HTTP layer before execution, so you get a transport error rather than a GraphQL response, and the cause is easy to misdiagnose.
saying these in an interview costs you the question
- Says GraphQL can only be sent over POST
- Passes variables as a nested object in the URL
- Thinks a mutation over GET is fine if the server allows it
- Forgets to percent-encode the document text
- Assumes a GET request has no practical size limit
- Puts sensitive variable values in a URL without noticing