---
title: Enabling Defer and Stream
sidebarTitle: Defer and Stream
---
import { Callout } from 'nextra/components';
# Enabling Defer and Stream
<Callout type="info">
`@defer`, `@stream`, and `experimentalExecuteIncrementally()` are available
in GraphQL.js v17 and newer. Incremental delivery is pending GraphQL
specification work.
</Callout>
`@defer` and `@stream` allow a GraphQL operation to produce an initial result
and later incremental payloads. This is useful when part of a response is slow,
large, or naturally delivered over time.
GraphQL.js keeps this feature explicit. You must add the directives to the
schema and use the experimental executor.
## Add the directives to your schema
If the `directives` option is passed to `GraphQLSchema`, the default directive
list is replaced. Include `specifiedDirectives` when adding experimental
directives.
```js
import {
GraphQLDeferDirective,
GraphQLSchema,
GraphQLStreamDirective,
specifiedDirectives,
} from 'graphql';
const schema = new GraphQLSchema({
query,
directives: [
...specifiedDirectives,
GraphQLDeferDirective,
GraphQLStreamDirective,
],
});
```
Once a schema includes `@defer` or `@stream`, execute operations against that
schema with `experimentalExecuteIncrementally()`. `execute()` is the
single-result executor and will reject schemas that contain the experimental
incremental directives.
## Execute incrementally
```js
import { experimentalExecuteIncrementally, parse } from 'graphql';
const document = parse(`
query ProductPage {
product(id: "abc") {
id
name
...Reviews @defer(label: "reviews")
}
}
fragment Reviews on Product {
reviews {
body
rating
}
}
`);
const result = await experimentalExecuteIncrementally({
schema,
document,
});
```
The result is either a normal `ExecutionResult` or an incremental result object.
```js
if ('initialResult' in result) {
sendInitialPayload(result.initialResult);
for await (const subsequentResult of result.subsequentResults) {
sendIncrementalPayload(subsequentResult);
}
} else {
sendSinglePayload(result);
}
```
GraphQL.js produces the execution results; your server transport is responsible
for serializing and delivering them to the client.
## Transport framing guidance
`experimentalExecuteIncrementally()` gives you result objects, not a wire
protocol. Pick a transport framing format that your clients already support and
test it end to end.
- HTTP multipart responses for clients that support incremental patches.
- Server-sent events when your stack already uses event streams.
- WebSocket message streams for subscription-like transports.
Keep transport concerns separate from execution concerns: validate operation
behavior first, then validate framing and client reassembly separately.
## `@defer`
`@defer` can be applied to fragment spreads and inline fragments. It defers the
fragment when `if` is `true` or omitted.
```graphql
query ProductPage($includeReviews: Boolean! = true) {
product(id: "abc") {
id
name
...Reviews @defer(if: $includeReviews, label: "reviews")
}
}
```
The `label` argument is optional, but labels must be unique for active
`@defer` and `@stream` usages in the operation.
## `@stream`
`@stream` can be applied to list fields. It sends `initialCount` items in the
initial result and streams later items in subsequent payloads.
```graphql
query Feed {
feed(first: 100) @stream(initialCount: 10, label: "feed") {
id
title
}
}
```
`initialCount` is non-null and defaults to `0`.
Resolvers may return normal iterables, promises, or async iterables for list
fields. Async iterables are especially useful with `@stream` because the
executor can complete list items as they become available.
## Early execution
`enableEarlyExecution` allows deferred work to begin before all non-deferred
work has completed.
```js
const result = await experimentalExecuteIncrementally({
schema,
document,
enableEarlyExecution: true,
});
```
This can reduce total latency for expensive deferred sections, but it can also
increase concurrent work. Measure before enabling it broadly.
## Validation limits
GraphQL.js validates the current incremental delivery rules:
- `@defer` and `@stream` are not supported on subscription operations.
- `@stream` must be used on list fields.
- `@stream(initialCount:)` must be non-null.
- Active `@defer` and `@stream` labels must be unique.
- Root field usage must follow the current proposal rules.
- Multiple active `@stream` instances cannot target the same field instance.
Those checks are exposed through the validation rules
`DeferStreamDirectiveLabelRule`, `DeferStreamDirectiveOnRootFieldRule`,
`DeferStreamDirectiveOnValidOperationsRule`, and
`StreamDirectiveOnListFieldRule`.
If a fragment is shared between query and subscription operations, use the
directive `if` argument to disable incremental behavior in the subscription.
```graphql
subscription Events($incremental: Boolean! = false) {
event {
...EventFields @defer(if: $incremental)
}
}
```
## Cancellation
Incremental execution accepts `abortSignal`. Aborting stops new payload
production and attempts to close async iterators.
```js
const controller = new AbortController();
const result = await experimentalExecuteIncrementally({
schema,
document,
abortSignal: controller.signal,
});
```