Available since Router 0.328.0.
What @defer does
When resolving a regular GraphQL response, a GraphQL server waits for every field to resolve. The response latency equals the resolve time of the slowest field. This can be a problem for large queries with many fields. One solution splits the query into smaller queries. The client then maintains two queries and makes two HTTP calls. With the@defer directive, a client marks fragments for later delivery.
The router sends fields outside active @defer fragments in the initial response.
The router sends deferred fields in later parts of the same multipart HTTP response.
The client renders the initial data immediately.
The client adds each deferred fragment when it arrives.
The merged result is identical to the result of the same query without @defer.
- The
employeessubgraph returnsemployeeandteammates. - The
productssubgraph returnsproducts. - The
hobbiessubgraph returnshobbies.
@defer, the router waits for all three subgraphs before it sends the response.
With @defer, the client receives everything except hobbies in the initial response.
The client receives hobbies in an incremental response.
Specification status
@defer is not yet part of a released GraphQL specification.
The router implements the Incremental Delivery spec draft.
The draft describes the response format as the pending, incremental, and completed format.
graphql-js 17.0.0-alpha.9 and Apollo Client 4.1 implement the same format.
Apollo Client implements it through its GraphQL17Alpha9Handler.
See Using @defer for client setup.
How to enable it
The router ignores@defer by default.
It treats deferred fragments as regular fragments and returns one JSON response.
To enable it, add the following to the router configuration:
config.yaml
ENGINE_ENABLE_DEFER.
Send a request
A client opts into incremental delivery with theAccept header:
multipart/mixedwithout a format version.multipart/mixed;incrementalSpec=v0.2, the format version the router implements.- The wildcards
multipart/*and*/*. - No
Acceptheader. The router then treats the request as one that accepts every form.
Accept header can allow none of these forms.
If the operation contains an active @defer, the router answers with status 200 and a GraphQL error:
multipart/mixed;deferSpec=20220824.
Apollo Router uses that older format.
Such a client parses the response incorrectly and loses the deferred data.
The router rejects the request with status 200 and the error code DEFER_UNSUPPORTED_SPEC:
Accept: multipart/mixed;deferSpec=20220824, */*, because the client stated which format it can parse.
@defer(if: false) disables that directive.
If every @defer directive is inactive, the router executes the operation without incremental delivery.
The router returns one JSON response regardless of the Accept header.
Response format
The router responds with these headers:Transfer-Encoding: chunked on HTTP/1.1 only.
HTTP/2 forbids the header and frames the stream itself.
Each part starts with the boundary --graphql, a Content-Type: application/json header, and an empty line.
One JSON payload follows.
The stream ends with the closing boundary --graphql--.
Line breaks inside the framing are \r\n.
Every part carries a plain GraphQL payload.
This differs from multipart subscriptions.
There, each part wraps the result in a payload object.
The query above produces this response:
Payload fields
A client merges an
incremental entry into its result at the location pending[id].path, followed by incremental.subPath.
The router adds a subPath in two cases.
The fragment is inside a list, and the subPath carries the list index.
The delivered object is deeper than the path of the fragment, as in the duplicated fields example.
The router announces a nested @defer in the pending list of the incremental response that delivers its parent fragment.
Proxies and load balancers
Incremental delivery needs the client to receive each part as soon as the router writes it. Every intermediary between the router and the client must pass each part through without buffering. The router setsX-Accel-Buffering: no.
This header disables buffering in NGINX and in compatible proxies.
Check ingress controllers, CDNs, and compression settings separately.
Next steps
Using @defer
Query patterns, response walkthroughs, and client setup for curl, fetch, and Apollo Client.
How the Router Executes @defer
What happens between receiving the query and sending the last part.
When to Use @defer
Choosing between @defer, separate queries, pagination, and subscriptions.
@defer directive reference
Definition, arguments, locations, and rules.