--- title: Enabling Defer and Stream sidebarTitle: Defer and Stream --- import { Callout } from 'nextra/components'; # Enabling Defer and Stream `@defer`, `@stream`, and `experimentalExecuteIncrementally()` are available in GraphQL.js v17 and newer. Incremental delivery is pending GraphQL specification work. `@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: - Active `@defer` and `@stream` are not supported on subscription operations, including through named fragments. - `@stream` must be used on list fields. - `@stream(initialCount:)` must be non-null. - Active `@defer` and `@stream` labels must be unique. - Root-field restrictions apply only to mutation and subscription root selections, including through fragments whose type condition matches the root type through an interface. Query root selections may use incremental delivery. - 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 a variable-backed `if` argument on `@defer` or `@stream`, or applicable `@skip` and `@include` directives, 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, }); ```