Skip to content

Incremental Delivery spec draft #1110

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 41 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
7ff9d0b
Extract common logic from ExecuteQuery, ExecuteMutation and ExecuteSu…
benjie Apr 28, 2023
8f4168b
Change ExecuteSelectionSet to ExecuteGroupedFieldSet
benjie Apr 28, 2023
69de3fd
Correct reference to MergeSelectionSets
benjie Aug 21, 2023
667364a
moves Field Collection section earlier
yaacovCR Feb 15, 2024
bb23a1b
Introduce `@defer` directive
yaacovCR Feb 15, 2024
65be49a
refactor a few lines out of YieldSubsequentResults
yaacovCR Jun 13, 2024
12513e1
add a word or two about which child nodes are being promoted
yaacovCR Jun 18, 2024
7284e2d
be more graphy
yaacovCR Jun 18, 2024
3fd7b90
fix timing
yaacovCR Jun 19, 2024
d3ab7a3
reuse function
yaacovCR Jun 19, 2024
853b031
fix
yaacovCR Jun 19, 2024
57d6193
rename BuildGraph to GraphFromRecords
yaacovCR Jun 19, 2024
fe42e8c
reword recursive abort case
yaacovCR Jun 19, 2024
d8966dc
bring BuildFieldPlan in line with implementation
yaacovCR Jul 17, 2024
136afea
rename "deferred grouped field set record" to "execution group"
yaacovCR Jul 17, 2024
f11f956
rename ExecuteExecutionGroup to CollectExecutionGroup
yaacovCR Jul 17, 2024
5e0a10a
properly initialize deferUsages with their parents
yaacovCR Jul 18, 2024
5490ed1
move Field Collection back to where it was
yaacovCR Jul 18, 2024
22dfbb8
f
yaacovCR Jul 18, 2024
b80d53b
use fieldDetailsList consistently
yaacovCR Jul 18, 2024
66536f4
add info re: data structures
yaacovCR Jul 18, 2024
a88da21
rename FieldPlan to ExecutionPlan
yaacovCR Jul 20, 2024
741605b
path => label
yaacovCR Jul 24, 2024
7251c7b
add missing arguments
yaacovCR Jul 25, 2024
2d121f1
add missing return value
yaacovCR Jul 25, 2024
c2d83a0
fix some renaming around CollectExecutionGroups and ExecuteExecutionG…
yaacovCR Jul 25, 2024
ccc26f2
Correct argument name
yaacovCR Aug 26, 2024
879818f
clarify errors from ExecuteExecutionPlan
yaacovCR Aug 26, 2024
a020ea1
add initial versions of explanations for the algorithms in the "Execu…
yaacovCR Aug 26, 2024
a6c164d
add subheadings
yaacovCR Sep 5, 2024
f15235a
adjust heading
yaacovCR Sep 6, 2024
50f644d
Initialize graph
yaacovCR Sep 6, 2024
afe40cd
adjust YieldSubsequentResults algorithm per review
yaacovCR Sep 6, 2024
606a6f1
reuse GetIncrementalResult() for the error case
yaacovCR Sep 6, 2024
f44deb5
add descriptions and fix bug within GetNewRootNodes, it needs the old…
yaacovCR Sep 6, 2024
f8f6f35
finish addressing review comments
yaacovCR Sep 6, 2024
4f8e668
add missing word
yaacovCR Sep 6, 2024
89983ce
Add Response Section for defer/stream (#4)
robrichard Sep 18, 2024
d40130d
Add directives and validation sections (#5)
robrichard Sep 18, 2024
f7e9124
Add examples to Response section
robrichard Nov 1, 2024
8e9cbcb
skip deferred fragment spread when already in visited fragments
robrichard Jan 9, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cspell.yml
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ ignoreRegExpList:
- /[a-z]{2,}'s/
words:
# Terms of art
- deprioritization
- endianness
- interoperation
- monospace
106 changes: 104 additions & 2 deletions spec/Section 3 -- Type System.md
Original file line number Diff line number Diff line change
@@ -794,8 +794,9 @@ And will yield the subset of each object type queried:
When querying an Object, the resulting mapping of fields are conceptually
ordered in the same order in which they were encountered during execution,
excluding fragments for which the type does not apply and fields or fragments
that are skipped via `@skip` or `@include` directives. This ordering is
correctly produced when using the {CollectFields()} algorithm.
that are skipped via `@skip` or `@include` directives or temporarily skipped via
`@defer`. This ordering is correctly produced when using the {CollectFields()}
algorithm.

Response serialization formats capable of representing ordered maps should
maintain this ordering. Serialization formats which can only represent unordered
@@ -1942,6 +1943,11 @@ by a validator, executor, or client tool such as a code generator.

GraphQL implementations should provide the `@skip` and `@include` directives.

GraphQL implementations are not required to implement the `@defer` and `@stream`
directives. If either or both of these directives are implemented, they must be
implemented according to this specification. GraphQL implementations that do not
support these directives must not make them available via introspection.

GraphQL implementations that support the type system definition language must
provide the `@deprecated` directive if representing deprecated portions of the
schema.
@@ -2162,3 +2168,99 @@ to the relevant IETF specification.
```graphql example
scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122")
```

### @defer

```graphql
directive @defer(
label: String
if: Boolean! = true
) on FRAGMENT_SPREAD | INLINE_FRAGMENT
```

The `@defer` directive may be provided for fragment spreads and inline fragments
to inform the executor to delay the execution of the current fragment to
indicate deprioritization of the current fragment. A query with `@defer`
directive will cause the request to potentially return multiple responses, where
non-deferred data is delivered in the initial response and data deferred is
delivered in a subsequent response. `@include` and `@skip` take precedence over
`@defer`.

```graphql example
query myQuery($shouldDefer: Boolean) {
user {
name
...someFragment @defer(label: "someLabel", if: $shouldDefer)
}
}
fragment someFragment on User {
id
profile_picture {
uri
}
}
```

#### @defer Arguments

- `if: Boolean! = true` - When `true`, fragment _should_ be deferred (see
related note below). When `false`, fragment will not be deferred and data will
be included in the initial response. Defaults to `true` when omitted.
- `label: String` - May be used by GraphQL clients to identify the data from
responses and associate it with the corresponding defer directive. If
provided, the GraphQL service must add it to the corresponding pending object
in the response. `label` must be unique label across all `@defer` and
`@stream` directives in a document. `label` must not be provided as a
variable.

### @stream

```graphql
directive @stream(
label: String
if: Boolean! = true
initialCount: Int = 0
) on FIELD
```

The `@stream` directive may be provided for a field of `List` type so that the
backend can leverage technology such as asynchronous iterators to provide a
partial list in the initial response, and additional list items in subsequent
responses. `@include` and `@skip` take precedence over `@stream`.

```graphql example
query myQuery($shouldStream: Boolean) {
user {
friends(first: 10) {
nodes @stream(label: "friendsStream", initialCount: 5, if: $shouldStream)
}
}
}
```

#### @stream Arguments

- `if: Boolean! = true` - When `true`, field _should_ be streamed (see related
note below). When `false`, the field will not be streamed and all list items
will be included in the initial response. Defaults to `true` when omitted.
- `label: String` - May be used by GraphQL clients to identify the data from
responses and associate it with the corresponding stream directive. If
provided, the GraphQL service must add it to the corresponding pending object
in the response. `label` must be unique label across all `@defer` and
`@stream` directives in a document. `label` must not be provided as a
variable.
- `initialCount: Int` - The number of list items the service should return as
part of the initial response. If omitted, defaults to `0`. A field error will
be raised if the value of this argument is less than `0`.

Note: The ability to defer and/or stream parts of a response can have a
potentially significant impact on application performance. Developers generally
need clear, predictable control over their application's performance. It is
highly recommended that GraphQL services honor the `@defer` and `@stream`
directives on each execution. However, the specification allows advanced use
cases where the service can determine that it is more performant to not defer
and/or stream. Therefore, GraphQL clients _must_ be able to process a response
that ignores the `@defer` and/or `@stream` directives. This also applies to the
`initialCount` argument on the `@stream` directive. Clients _must_ be able to
process a streamed response that contains a different number of initial list
items than what was specified in the `initialCount` argument.
181 changes: 180 additions & 1 deletion spec/Section 5 -- Validation.md
Original file line number Diff line number Diff line change
@@ -422,6 +422,7 @@ FieldsInSetCanMerge(set):
{set} including visiting fragments and inline fragments.
- Given each pair of members {fieldA} and {fieldB} in {fieldsForName}:
- {SameResponseShape(fieldA, fieldB)} must be true.
- {SameStreamDirective(fieldA, fieldB)} must be true.
- If the parent types of {fieldA} and {fieldB} are equal or if either is not
an Object Type:
- {fieldA} and {fieldB} must have identical field names.
@@ -456,6 +457,16 @@ SameResponseShape(fieldA, fieldB):
- If {SameResponseShape(subfieldA, subfieldB)} is {false}, return {false}.
- Return {true}.

SameStreamDirective(fieldA, fieldB):

- If neither {fieldA} nor {fieldB} has a directive named `stream`.
- Return {true}.
- If both {fieldA} and {fieldB} have a directive named `stream`.
- Let {streamA} be the directive named `stream` on {fieldA}.
- Let {streamB} be the directive named `stream` on {fieldB}.
- If {streamA} and {streamB} have identical sets of arguments, return {true}.
- Return {false}.

Note: In prior versions of the spec the term "composite" was used to signal a
type that is either an Object, Interface or Union type.

@@ -467,7 +478,7 @@ unambiguous. Therefore any two field selections which might both be encountered
for the same object are only valid if they are equivalent.

During execution, the simultaneous execution of fields with the same response
name is accomplished by {MergeSelectionSets()} and {CollectFields()}.
name is accomplished by {CollectSubfields()}.

For simple hand-written GraphQL, this rule is obviously a clear developer error,
however nested fragments can make this difficult to detect manually.
@@ -1521,6 +1532,174 @@ query ($foo: Boolean = true, $bar: Boolean = false) {
}
```

### Defer And Stream Directives Are Used On Valid Root Field

** Formal Specification **

- For every {directive} in a document.
- Let {directiveName} be the name of {directive}.
- Let {mutationType} be the root Mutation type in {schema}.
- Let {subscriptionType} be the root Subscription type in {schema}.
- If {directiveName} is "defer" or "stream":
- The parent type of {directive} must not be {mutationType} or
{subscriptionType}.

**Explanatory Text**

The defer and stream directives are not allowed to be used on root fields of the
mutation or subscription type.

For example, the following document will not pass validation because `@defer`
has been used on a root mutation field:

```raw graphql counter-example
mutation {
... @defer {
mutationField
}
}
```

### Defer And Stream Directives Are Used On Valid Operations

** Formal Specification **

- Let {subscriptionFragments} be the empty set.
- For each {operation} in a document:
- If {operation} is a subscription operation:
- Let {fragments} be every fragment referenced by that {operation}
transitively.
- For each {fragment} in {fragments}:
- Let {fragmentName} be the name of {fragment}.
- Add {fragmentName} to {subscriptionFragments}.
- For every {directive} in a document:
- If {directiveName} is not "defer" or "stream":
- Continue to the next {directive}.
- Let {ancestor} be the ancestor operation or fragment definition of
{directive}.
- If {ancestor} is a fragment definition:
- If the fragment name of {ancestor} is not present in
{subscriptionFragments}:
- Continue to the next {directive}.
- If {ancestor} is not a subscription operation:
- Continue to the next {directive}.
- Let {if} be the argument named "if" on {directive}.
- {if} must be defined.
- Let {argumentValue} be the value passed to {if}.
- {argumentValue} must be a variable, or the boolean value "false".

**Explanatory Text**

The defer and stream directives can not be used to defer or stream data in
subscription operations. If these directives appear in a subscription operation
they must be disabled using the "if" argument. This rule will not permit any
defer or stream directives on a subscription operation that cannot be disabled
using the "if" argument.

For example, the following document will not pass validation because `@defer`
has been used in a subscription operation with no "if" argument defined:

```raw graphql counter-example
subscription sub {
newMessage {
... @defer {
body
}
}
}
```

### Defer And Stream Directive Labels Are Unique

** Formal Specification **

- Let {labelValues} be an empty set.
- For every {directive} in the document:
- Let {directiveName} be the name of {directive}.
- If {directiveName} is "defer" or "stream":
- For every {argument} in {directive}:
- Let {argumentName} be the name of {argument}.
- Let {argumentValue} be the value passed to {argument}.
- If {argumentName} is "label":
- {argumentValue} must not be a variable.
- {argumentValue} must not be present in {labelValues}.
- Append {argumentValue} to {labelValues}.

**Explanatory Text**

The `@defer` and `@stream` directives each accept an argument "label". This
label may be used by GraphQL clients to uniquely identify response payloads. If
a label is passed, it must not be a variable and it must be unique within all
other `@defer` and `@stream` directives in the document.

For example the following document is valid:

```graphql example
{
dog {
...fragmentOne
...fragmentTwo @defer(label: "dogDefer")
}
pets @stream(label: "petStream") {
name
}
}

fragment fragmentOne on Dog {
name
}

fragment fragmentTwo on Dog {
owner {
name
}
}
```

For example, the following document will not pass validation because the same
label is used in different `@defer` and `@stream` directives.:

```raw graphql counter-example
{
dog {
...fragmentOne @defer(label: "MyLabel")
}
pets @stream(label: "MyLabel") {
name
}
}
fragment fragmentOne on Dog {
name
}
```

### Stream Directives Are Used On List Fields

**Formal Specification**

- For every {directive} in a document.
- Let {directiveName} be the name of {directive}.
- If {directiveName} is "stream":
- Let {adjacent} be the AST node the directive affects.
- {adjacent} must be a List type.

**Explanatory Text**

GraphQL directive locations do not provide enough granularity to distinguish the
type of fields used in a GraphQL document. Since the stream directive is only
valid on list fields, an additional validation rule must be used to ensure it is
used correctly.

For example, the following document will only pass validation if `field` is
defined as a List type in the associated schema.

```graphql counter-example
query {
field @stream(initialCount: 0)
}
```

## Variables

### Variable Uniqueness
637 changes: 571 additions & 66 deletions spec/Section 6 -- Execution.md

Large diffs are not rendered by default.

406 changes: 395 additions & 11 deletions spec/Section 7 -- Response.md
Original file line number Diff line number Diff line change
@@ -10,7 +10,12 @@ the case that any _field error_ was raised on a field and was replaced with

## Response Format

A response to a GraphQL request must be a map.
A response to a GraphQL request must be a map or a stream of incrementally
delivered results. The response will be a stream of incrementally delivered
results when the GraphQL service has deferred or streamed data as a result of
the `@defer` or `@stream` directives. When the response of the GraphQL operation
contains incrementally delivered results, the first value will be an initial
payload, followed by one or more subsequent payloads.

If the request raised any errors, the response map must contain an entry with
key `errors`. The value of this entry is described in the "Errors" section. If
@@ -22,14 +27,31 @@ key `data`. The value of this entry is described in the "Data" section. If the
request failed before execution, due to a syntax error, missing information, or
validation error, this entry must not be present.

When the response of the GraphQL operation contains incrementally delivered
results, both the initial payload and all subsequent payloads must contain an
entry with key `hasNext`. The value of this entry must be {true} for all but the
last response in the stream. The value of this entry must be {false} for the
last response of the stream. This entry must not be present for GraphQL
operations that return a single response map.

When the response of the GraphQL operation contains incrementally delivered
results, both the initial payload and any subsequent payloads may contain
entries with the keys `pending`, `incremental`, and/or `completed`. The value of
these entries are described in the "Pending", "Incremental", and "Completed"
sections below.

The response map may also contain an entry with key `extensions`. This entry, if
set, must have a map as its value. This entry is reserved for implementers to
extend the protocol however they see fit, and hence there are no additional
restrictions on its contents.
restrictions on its contents. When the response of the GraphQL operation is a
response stream, the initial payload and any subsequent payloads may contain an
entry with the key `extensions`, also reserved for implementers to extend the
protocol however they see fit. Additionally, implementers may send subsequent
payloads containing only `hasNext` and `extensions` entries.

To ensure future changes to the protocol do not break existing services and
clients, the top level response map must not contain any entries other than the
three described above.
entries described above.

Note: When `errors` is present in the response, it may be helpful for it to
appear first when serialized to make it more clear when errors are present in a
@@ -48,6 +70,10 @@ present in the result.
If an error was raised during the execution that prevented a valid response, the
`data` entry in the response should be `null`.

When the response of the GraphQL operation contains incrementally delivered
results, `data` may only be present in the initial payload. `data` must not be
present in any subsequent payloads.

### Errors

The `errors` entry in the response is a non-empty list of errors raised during
@@ -107,14 +133,8 @@ syntax element.
If an error can be associated to a particular field in the GraphQL result, it
must contain an entry with the key `path` that details the path of the response
field which experienced the error. This allows clients to identify whether a
`null` result is intentional or caused by a runtime error.

If present, this field must be a list of path segments starting at the root of
the response and ending with the field associated with the error. Path segments
that represent fields must be strings, and path segments that represent list
indices must be 0-indexed integers. If the error happens in an aliased field,
the path to the error must use the aliased name, since it represents a path in
the response, not in the request.
`null` result is intentional or caused by a runtime error. The value of this
field is described in the [Path](#sec-Path) section.

For example, if fetching one of the friends' names fails in the following
operation:
@@ -244,6 +264,366 @@ discouraged.
}
```

### Path

A `path` field allows for the association to a particular field in a GraphQL
result. This field should be a list of path segments starting at the root of the
response and ending with the field to be associated with. Path segments that
represent fields should be strings, and path segments that represent list
indices should be 0-indexed integers. If the path is associated to an aliased
field, the path should use the aliased name, since it represents a path in the
response, not in the request.

When the `path` field is present on an "Error result", it indicates the response
field which experienced the error.

### Pending

The `pending` entry in the response is a non-empty list of Pending Results. If
the response of the GraphQL operation contains incrementally delivered results,
this field may appear on both the initial and subsequent payloads. If present,
the `pending` entry must contain at least one Pending Result.

Each Pending Result corresponds to either a `@defer` or `@stream` directive
located at a specific path in the response data. The Pending Result is used to
communicate that the GraphQL service has chosen to incrementally deliver the
data associated with this `@defer` or `@stream` directive and clients should
expect the associated data in either the current payload, or one of the
following payloads.

**Pending Result Format**

Every Pending Result must contain an entry with the key `id` with a string
value. This `id` should be used by clients to correlate Pending Results with
Completed Results. The `id` value must be unique for the entire response stream.
There must not be any other Pending Result in any payload that contains the same
`id`.

Every Pending Result must contain an entry with the key `path`. When the Pending
Result is associated with a `@stream` directive, it indicates the response list
field that is not known to be complete. Clients should expect the GraphQL
Service to incrementally deliver the remainder of indicated list field. When the
Pending Result is associated with a `@defer` directive, it indicates that the
response fields contained in the deferred fragment are not known to be complete.
Clients should expect the the GraphQL Service to incrementally deliver the
remainder of the fields contained in the deferred fragment.

If the associated `@defer` or `@stream` directive contains a `label` argument,
the Pending Result must contain an entry `label` with the value of this
argument.

If a Pending Result is not returned for a `@defer` or `@stream` directive,
clients must assume that the GraphQL service chose not to incrementally deliver
this data, and the data can be found either in the `data` entry in the initial
payload, or one of the Incremental Results in a prior payload.

### Incremental

The `incremental` entry in the response is a non-empty list of Incremental
Results. If the response of the GraphQL operation contains incrementally
delivered results, this field may appear on both the initial and subsequent
values. If present, the `incremental` entry must contain at least one
Incremental Result.

The Incremental Result is used to deliver data that the GraphQL service has
chosen to incrementally deliver. An Incremental Result may be ether an
Incremental List Result or an Incremental Object Result.

An Incremental List Result is used to deliver additional list items for a list
field with a `@stream` directive.

An Incremental Object Result is used to deliver additional response fields that
were contained in one or more fragments with a `@defer` directive.

**Incremental Result Format**

Every Incremental Result must contain an entry with the key `id` with a string
value. This `id` must match the `id` that was returned in a prior Pending
Result.

Additionally, Incremental List Results and Incremental Object Results have
further requirements.

**Incremental List Result Format**

An Incremental List Result's `id` entry must match the `id` that was returned in
a prior Pending Result. This Pending Result must be associated with a `@stream`
directive.

The Incremental List Result's `path` can be determined using the prior Pending
Result with the same `id` as this Incremental Result. The Incremental List
Result's `path` is the same as the Pending Result's `path`.

Every Incremental List Result must contain an `items` entry. The `items` entry
must contain a list of additional list items for the response field at the
Incremental List Result's `path`. This output will be a list of the same type of
the response field at this path.

If any field errors were raised during the execution of the results in `items`
and these errors bubbled to a path higher than the Incremental List Result's
path, The Incremental List Result is considered failed and should not be
included in the response stream. The errors that caused this failure will be
included in a Completed Result.

If any field errors were raised during the execution of the results in `items`
and these errors did not bubble to a path higher than the Incremental List
Result's path, the Incremental List Result must contain an entry with key
`errors` containing these field errors. The value of this entry is described in
the "Errors" section.

**Incremental Object Result Format**

An Incremental List Result's `id` entry must match the `id` that was returned in
a prior Pending Result. This Pending Result must be associated with a `@defer`
directive.

The Incremental Object Result's `path` can be determined using the prior Pending
Result with the same `id` as this Incremental Result. The Incremental Object
Result may contain a `subPath` entry. If the `subPath` entry is present, The
Incremental Object Record's path can be determined by concatenating the Pending
Result's `path` with this `subPath`. If no `subPath` entry is present, the path
is the same as the Pending Result's `path`.

Every Incremental Object Result must contain an `data` entry. The `data` entry
must contain a map of additional response fields. The `data` entry in an
Incremental Object Result will be of the type of a particular field in the
GraphQL result. The Incremental Object Result's `path` will contain the path
segments of the field this data is associated with.

An Incremental Object Result's data may contain response fields that were
contained in more than one deferred fragments. In that case, the `id` of the
Incremental Object Result must point to the Pending Result that results in the
shortest `subPath`.

If any field errors were raised during the execution of the results in `data`
and these errors bubbled to a path higher than the Incremental Object Result's
path, The Incremental Object Result is considered failed and should not be
included in the response stream. The errors that caused this failure will be
included in a Completed Result.

If any field errors were raised during the execution of the results in `data`
and these errors did not bubble to a path higher than the Incremental Object
Result's path, the Incremental Object Result must contain an entry with key
`errors` containing these field errors. The value of this entry is described in
the "Errors" section.

### Completed

The `completed` entry in the response is a non-empty list of Completed Results.
If the response of the GraphQL operation contains incrementally delivered
results, this field may appear on both the initial and subsequent payloads. If
present, the `completed` entry must contain at least one Completed Result.

Each Completed Result corresponds to a prior Pending Result. The Completed
Result is used to communicate that the GraphQL service has completed the
incremental delivery of the data associated with the corresponding Pending
Result. The associated data must have been completed in the current payload.

**Completed Result Format**

Every Completed Result must contain an entry with the key `id` with a string
value. The `id` entry must match the `id` that was returned in a prior Pending
Result.

A Completed Result may contain an `errors` entry. When the `errors` entry is
present, it informs clients that the delivery of the data associated with the
corresponding Pending Result has failed, due to an error bubbling to a path
higher than the Incremental Data Result's path. The `errors` entry must contain
these field errors. The value of this entry is described in the "Errors"
section.

### Examples

#### A query containing both defer and stream:

```graphql example
query {
person(id: "cGVvcGxlOjE=") {
...HomeWorldFragment @defer(label: "homeWorldDefer")
name
films @stream(initialCount: 1, label: "filmsStream") {
title
}
}
}
fragment HomeWorldFragment on Person {
homeWorld {
name
}
}
```

The response stream might look like:

Payload 1, the initial response does not contain any deferred or streamed
results in the `data` entry. The initial response contains a `hasNext` entry,
indicating that subsequent payloads will be delivered. There are two Pending
Responses indicating that results for both the `@defer` and `@stream` in the
query will be delivered in the subsequent payloads.

```json example
{
"data": {
"person": {
"name": "Luke Skywalker",
"films": [{ "title": "A New Hope" }]
}
},
"pending": [
{ "id": "0", "path": ["person"], "label": "homeWorldDefer" },
{ "id": "1", "path": ["person", "films"], "label": "filmsStream" }
],
"hasNext": true
}
```

Payload 2, contains the deferred data and the first streamed list item. There is
one Completed Result, indicating that the deferred data has been completely
delivered.

```json example
{
"incremental": [
{
"id": "0",
"data": { "homeWorld": { "name": "Tatooine" } }
},
{
"id": "1",
"items": [{ "title": "The Empire Strikes Back" }]
}
],
"completed": [
{"id": "0"}
]
"hasNext": true
}
```

Payload 3, contains the final stream payload. In this example, the underlying
iterator does not close synchronously so {hasNext} is set to {true}. If this
iterator did close synchronously, {hasNext} would be set to {false} and this
would be the final response.

```json example
{
"incremental": [
{
"id": "1",
"items": [{ "title": "Return of the Jedi" }]
}
],
"hasNext": true
}
```

Payload 4, contains no incremental data. {hasNext} set to {false} indicates the
end of the response stream. This response is sent when the underlying iterator
of the `films` field closes.

```json example
{
"hasNext": false
}
```

### Examples

#### A query containing overlapping defers:

```graphql example
query {
person(id: "cGVvcGxlOjE=") {
...HomeWorldFragment @defer(label: "homeWorldDefer")
...NameAndHomeWorldFragment @defer(label: "nameAndWorld")
firstName
}
}
fragment HomeWorldFragment on Person {
homeWorld {
name
terrain
}
}

fragment NameAndHomeWorldFragment on Person {
firstName
lastName
homeWorld {
name
}
}
```

The response stream might look like:

Payload 1, the initial response contains the results of the `firstName` field.
Even though it is also present in the `HomeWorldFragment`, it must be returned
in the initial payload because it is also defined outside of any fragments with
the `@defer` directive. Additionally, There are two Pending Responses indicating
that results for both `@defer`s in the query will be delivered in the subsequent
payloads.

```json example
{
"data": {
"person": {
"firstName": "Luke"
}
},
"pending": [
{ "id": "0", "path": ["person"], "label": "homeWorldDefer" },
{ "id": "1", "path": ["person"], "label": "nameAndWorld" }
],
"hasNext": true
}
```

Payload 2, contains the deferred data from `HomeWorldFragment`. There is one
Completed Result, indicating that `HomeWorldFragment` has been completely
delivered. Because the `homeWorld` field is present in two separate `@defer`s,
it is separated into its own Incremental Result.

The second Incremental Result contains the data for the `terrain` field. This
incremental result contains a `subPath` property to indicate to clients that the
path of this result can be determined by concatenating the path from the Pending
Result with id `"0"` and this `subPath` entry.

```json example
{
"incremental": [
{
"id": "0",
"data": { "homeWorld": { "name": "Tatooine" } }
},
{
"id": "0",
"subPath": ["homeWorld"],
"data": { "terrain": "desert" }
}
],
"completed": [{ "id": "0" }],
"hasNext": true
}
```

Payload 3, contains the remaining data from the `NameAndHomeWorldFragment`.
`lastName` is the only remaining field that has not been delivered in a previous
payload.

```json example
{
"incremental": [
{
"id": "1",
"data": { "lastName": "Skywalker" }]
}
],
"completed": [{"id": "1"}],
"hasNext": false
}
```

## Serialization Format

GraphQL does not require a specific serialization format. However, clients
@@ -320,3 +700,7 @@ encode the same value, they also have observably different property orderings.

Note: This does not violate the JSON spec, as clients may still interpret
objects in the response as unordered Maps and arrive at a valid value.

```
```