What do the @defer and @stream directives change about a single GraphQL response?
answer
- one operation, more than one payload
- the fast fields do not wait
- marks a fragment, never a plain field
- initialCount ships the first list items
- spec-track, in no released edition
basics
~20 sOne operation answers in several payloads instead of one. The server returns the fields it can answer immediately, then sends a deferred fragment's data, or a streamed list's remaining items, as they finish. Neither directive is in a released specification edition.
solid answer
~50 s`@defer` and `@stream` are executable directives a client writes in its document to say *this part may arrive late*. `@defer` goes on a fragment spread or an inline fragment; `@stream` goes on a list field and takes `initialCount`, the number of items to include up front. The server answers with an initial payload holding everything that was not deferred, marked as having more to come, then one payload per deferred fragment or streamed slice, each carrying the response `path` where it belongs, and finally a payload that marks the response finished. The client merges each payload into the tree it already holds. Both directives are spec-track only: no released edition of the GraphQL specification defines them, the payload envelope has been revised across drafts, and the framing — usually `multipart/mixed` parts in one response body — is convention rather than a ratified rule.
code
graphql · 16 linesquery AccountOverview($id: ID!) {
account(id: $id) {
displayName
availableBalance
statements(last: 12) @stream(initialCount: 2) {
period
closingBalance
}
... on Account @defer(label: "spendBreakdown") {
spendBreakdown {
category
total
}
}
}
}go deeper
Be ready to say in one sentence what changes: one operation, several payloads, fast fields first. Know that @defer marks a fragment and @stream marks a list field, and that neither is in a released specification edition.
Explain the mechanics: the initial payload omits deferred fields entirely rather than nulling them, later payloads carry a path and a label, and it is all one HTTP response body rather than a second request.
Show you know the operational consequences — the status code is committed before the deferred fields run, and support has to exist end to end in server, client and everything the body travels through.
Own the adoption question. These are unratified directives whose payload envelope has changed across drafts, so argue when the latency win justifies betting on spec-track behaviour rather than splitting the screen into two operations.
## The problem incremental delivery exists to solve A GraphQL operation is normally answered by exactly one response, and that response cannot be serialized until every selected field has resolved. The consequence is arithmetic: the slowest field in the document sets the arrival time of all the others. A screen that shows an account's name, its balance, and a twelve-month spending breakdown waits on the breakdown, even though the first two were ready in milliseconds. The usual fixes are to split the screen into two operations, or to make the slow field fast. Incremental delivery is a third option — keep one operation, but let its answer arrive in pieces. ## The two directives `@defer` and `@stream` are **executable** directives: a client writes them in its document, and they change how the server delivers the result. They are not type-system directives and they do not appear in SDL. `@defer` is defined for **fragment spreads and inline fragments**, not for plain fields. To defer a single field you wrap it in an inline fragment on its parent type. It takes an optional `label`, a static string the client chooses so it can recognise the payload when it arrives, and an `if` argument whose Boolean, when false, makes the server execute the fragment as though the directive were absent. `@stream` is defined for **list fields**. Its `initialCount` argument says how many items ship with the initial payload; the remainder arrive afterwards. It carries the same `label` and `if` arguments. Both are requests, not commands. A server that does not implement them, or a client that has not negotiated an incremental transport, is expected to execute the document as an ordinary single-payload operation with the deferred fields included. ## What comes back The response becomes a sequence: 1. an **initial payload** carrying `data` for everything that was not deferred, with the deferred fragments simply *absent* from the tree — not present and null; 2. **zero or more later payloads**, each carrying the data for one deferred fragment or streamed slice plus the response `path` at which it belongs, and the `label` if one was given; 3. a final payload whose `hasNext` is `false`, the only signal that nothing more is coming. The client holds the initial tree and merges each later payload into it. Nothing is ever overwritten, because a deferred fragment's fields were never delivered in the first place. ## The transport There is no second HTTP response. All of this is one response body that the server flushes progressively, and the parts are conventionally framed as `multipart/mixed` with a boundary, each part carrying a JSON payload; `text/event-stream` is also used. The HTTP status line goes out with the initial payload, which has a consequence worth stating in an interview: **the status is committed before the later results are known**, so a resolver that fails in a deferred fragment cannot turn a 200 into anything else. Its failure arrives as an entry in a later payload's `errors`. Because of that framing, servers generally require the client to advertise support in `Accept` and fall back to one payload otherwise. ## Specification status — say this out loud Neither directive is in a **released edition** of the GraphQL specification. They are spec-track work: an RFC and a working draft, implemented ahead of ratification by some servers and clients. The envelope itself has been revised more than once across drafts — one revision grouped later payloads in an array, a later one added explicit bookkeeping for which deferred fragments are still pending — so the durable things to remember are the invariants: a path, an optional label, and a `hasNext` flag that terminates the response. Treating the exact key names as settled is how candidates get caught out. ## What it is not It is not a subscription. A subscription is a separate operation type whose result is an unbounded stream of independent results, one per source event, and it usually runs over a persistent connection. Incremental delivery is **one** result for **one** query or mutation, split into pieces, over one ordinary request. It is also not a way to do less work. Every deferred field still resolves, on the same server, hitting the same backends. The server may hold the request open longer and pays a little extra serialization and framing per payload. What moves is *when the client can paint*, not what the machine does. ## A worked shape For a bank statements graph, an overview screen selects the account's display name and available balance directly, streams the twelve statement summaries with `initialCount: 2` so the first two rows paint immediately, and defers an inline fragment containing the spending breakdown. The first payload lands with the header and two rows; the breakdown payload lands later carrying `path: ["account"]` and the label the client chose, and the client swaps a skeleton for a chart. That is the whole idea, and the interview follow-up is always the same: was the breakdown *actually* slow enough to justify a second payload and the merge logic behind it?
- Can @defer be applied to an ordinary field instead of a fragment?No. Its defined locations are fragment spreads and inline fragments, so to defer one field you wrap it in an inline fragment on its parent type. The practical effect is that the unit of deferral is a group of fields with a single label, which is also what makes the later payload easy to identify when it arrives.
- What is the `if` argument on @defer for?It is a Boolean that, when false, makes the server execute the fragment as though the directive were not there — the fields come back in the initial payload. Wired to a variable, it lets one stored document serve both a screen that wants progressive rendering and a caller that would rather have a single complete response.
- What happens to a document containing @defer if the client has not negotiated incremental delivery?By convention the server executes it as an ordinary single-payload operation with the deferred fields included, rather than failing. Support is normally advertised in the request's `Accept` header, and the fallback keeps one document usable against servers and callers that do not speak the multi-payload framing.
It is a restaurant bringing the drinks and bread out as soon as they are ready instead of holding the whole table's order until the slowest main is plated. The kitchen does not cook less; you just start eating sooner.
saying these in an interview costs you the question
- Says @defer is part of a released GraphQL specification edition
- Claims @defer makes the server do less total work
- Puts @defer on a plain field and expects it to validate
- Confuses incremental payloads with a subscription's event stream
- Expects the deferred data to arrive as a second HTTP response
- Thinks @stream reduces the number of backend calls