skip to content

What fixes the key order of a GraphQL response map when resolvers finish out of order?

level: middleimportance: nice to knowfreq 26%

answer

  1. Two orders exist; only one is visible
  2. Slots are reserved before any value arrives
  3. The document decides, not the clock
  4. Should preserve, where the format allows
  5. JSON objects are formally unordered

basics

~20 s

The document does. Keys appear in the order the fields were selected, with fragments flattened, no matter which resolver finished first. A serializer should preserve that order, but JSON defines no object ordering, so clients must not read by position.

solid answer

~50 s

Execution builds each object as an **ordered map** whose entry order comes from the grouped field set — that is, the order the fields appear in the executed document once fragments are flattened and same-key selections merged. Completion order has no influence: a field that took 1.4 seconds and a sibling that took 3 milliseconds still appear in selected order, and an alias occupies the position where the alias was written. The specification asks a serializer to preserve that order where the output format supports it. JSON objects, however, are formally unordered, so this is a courtesy for humans reading a response and for stable golden-file diffs — never a contract a client may parse positionally. Clients look keys up by name. Nested objects are each ordered by their own selection set, and list items keep the order of the source list rather than the order the items completed.

code

json · 9 lines
json
{
  "data": {
    "string": {
      "live": { "wattsDc": 4128 },
      "serial": "STR-8812",
      "yesterday": { "wattsDc": 3907 }
    }
  }
}

go deeper

for a junior

Remember the simple version: you get your fields back in the order you asked for them, whatever the server did internally. Look values up by name rather than by position.

for a middle

Explain the mechanism — the ordered map's slots come from the grouped field set before any value exists, so completion order cannot reorder anything — and note that aliases occupy the position where they were written.

for a senior

Draw the line between an internal convenience and an external contract: golden-file tests inside your system may lean on key order, a consumer across a boundary may not, because JSON gives no such guarantee and an intermediary may re-serialize.

for a principal

Be clear about what determinism the graph does and does not promise its consumers, so nobody builds tooling on incidental ordering — and make sure the errors list, which carries no ordering rule at all, is not being read as a timeline.

## Two different orders, and only one of them is yours to observe When an executor resolves the fields of one object it has, in effect, two orders in play. There is the order in which values become available — arbitrary, dependent on how slow each backend was that second, and different on every request. And there is the order in which entries appear in the map it is building — fixed before execution began. The second is the one the client sees, and conflating them is where the confusion lives. The specification's execution algorithm builds an **ordered map** for each object. Its keys are the response keys of the grouped field set, and the grouped field set is assembled by walking the selection set in the order the document wrote it, flattening fragments in place and merging repeated selections of the same response key into the position where that key first appeared. So the slot for each key exists before any resolver has returned anything. When a value finally arrives, the executor fills the slot that was already reserved for it. A field that completes first does not jump the queue. ## The alias case, which makes the rule visible Aliases give each selection its own response key, so they are the cleanest demonstration. In a solar-array telemetry graph: ```graphql query StringDetail { string(id: "STR-8812") { live: lastReading { wattsDc } # slow: hits the inverter poller serial # fast: already on the parent value yesterday: reading(day: -1) { wattsDc } } } ``` `serial` is read straight off the loaded record and is available immediately; `live` may take a second. The response still reads `live`, `serial`, `yesterday`, because those are the keys in the order the document wrote them. ## Nesting and lists The rule is per object, and it recurses. Each nested object is an ordered map built from *its own* selection set, so ordering inside `weatherStation` reflects how that inner selection set was written, independent of the outer one. Lists are a related but distinct guarantee, and it is a useful one to state explicitly because it is exactly the same misconception one level down. When a list field's items are completed concurrently, each item is completed at its own index and placed at that index. The resulting list is in the order of the source iterable the resolver returned — never in the order the items finished. If a client sees items in an order it did not expect, that is the resolver's ordering of its source data, not a race. ## What the specification says about serializing it The specification's response section asks that a serialized response preserve the ordering of the map where the serialization format supports it. Note the shape of that: it is a *should*, and it is conditional on the format. JSON, the format almost every GraphQL response is serialized to, formally treats an object as an unordered collection of members. Most JSON parsers happen to preserve insertion order, and virtually every server emits keys in the executed order — but a client that reads "the first key of `data`" or compares two responses byte-for-byte after passing them through an intermediary is depending on something neither JSON nor the specification promises. The practical line is easy to draw. **Within your own system, rely on it**: golden-file tests over a response body are stable, and a human reading a response sees fields in the shape they asked for, which is genuinely one of the pleasant properties of the protocol. **Across a boundary you do not own, do not**: address values by key, and treat any two orderings as equivalent. ## What carries no ordering rule at all The response's `errors` entry is a list, and the specification sets no requirement that its entries appear in field order, document order, or completion order. So do not infer from an errors list which field failed first, and do not write an assertion that expects two field errors in a particular sequence — sort by the `path` of each entry, or match on content, if you need determinism in a test. Similarly, nothing about the response map tells you anything about timing. Two adjacent keys may have been produced eleven seconds apart, or simultaneously on two threads. If you want to know how long a field took, that is a job for tracing instrumentation, not for reading the response.

  • If the items of a list field are completed concurrently, what fixes their order in the response?
    The order of the source list the resolver returned. Each item is completed under its own index and written to that index, so the item that finishes first does not move to the front. If a client needs a particular ordering, the resolver has to produce the source list in that order — or the schema has to expose a sort argument.
  • Is the order of entries in the response's errors list defined?
    No. The specification places no ordering requirement on that list, so its sequence tells you nothing about which field failed first or which resolver ran first. If a test needs determinism, sort the entries by their `path` or assert on content rather than on position.
  • Can a client rely on key order to parse a GraphQL response?
    It should not. The specification asks a serializer to preserve order only where the format supports it, and JSON treats object members as unordered. Any intermediary that re-serializes the body may reorder keys without breaking a single rule. Read values by key name; treat the ordering as a readability nicety.

saying these in an interview costs you the question

  • Assumes response keys arrive in completion order
  • Says the server sorts response keys alphabetically
  • Treats JSON object key order as a parseable contract
  • Thinks schema declaration order sets response order
  • Infers which field failed first from the errors list

context