-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Spec edits for incremental delivery, Response Section and Examples Appendix #1203
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
Merged
robrichard
merged 14 commits into
incremental-integration
from
incremental-integration-response
Apr 9, 2026
Merged
Changes from all commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
644df1d
Spec edits for incremental delivery, Examples
robrichard c54049c
Spec edits for incremental delivery, Section 7 only
robrichard ff9c30c
fix typos
robrichard 554bf1b
Update to address feedback in exampled appendix
robrichard 00c726b
Update terminology
robrichard 048315e
PR feedback
robrichard 0a424c1
allow hasNext: false on initial result
robrichard 1904ef4
pending is must on initial stream result
robrichard 03242f0
value => payload when describing what is yielded by a stream
robrichard 68a01de
Update description of _initial incremental stream result_ & _incremen…
robrichard b9d8e8a
fix incorrect term
robrichard ffacdf4
PR Feedback
robrichard e928e0f
Pending Result -> Incremental Pending Notice
robrichard 711f708
Completed Result -> Incremental Completion Notice
robrichard File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,219 @@ | ||
| # E. Appendix: Examples | ||
|
|
||
| ## Incremental Delivery Examples | ||
|
|
||
| ### Example 1 - 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 to this request will be an _incremental stream_ consisting of an | ||
| _initial incremental stream result_ followed by one or more _incremental stream | ||
| update result_. | ||
|
|
||
| The _initial incremental stream result_ has: | ||
|
|
||
| - a {"data"} entry containing the results of the GraphQL operation except for | ||
| the `@defer` and `@stream` selections; | ||
| - a {"pending"} entry containing two _incremental pending notices_, one for the | ||
| `@defer` selection and for the the `@stream` selection, indicating that these | ||
| results will be delivered in a later _incremental stream update result_; | ||
| - a {"hasNext"} entry with the value {true}, indicating that the response is not | ||
| yet complete. | ||
|
|
||
| If an error were to occur, it would also have an {"errors"} entry; but not in | ||
| this example. | ||
|
|
||
| ```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 | ||
| } | ||
| ``` | ||
|
|
||
| Depending on the behavior of the backend and the time at which the deferred and | ||
| streamed resources resolve, the stream may produce results in different orders. | ||
| In this example, our first _incremental stream update result_ contains the | ||
| deferred data and the first streamed list item. There is one _incremental | ||
| completion notice_, 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 | ||
| } | ||
| ``` | ||
|
|
||
| The second _incremental stream update result_ contains the final stream results. | ||
| In this example, the underlying iterator does not close synchronously so | ||
| {"hasNext"} is set to {true}. If this iterator did close synchronously, | ||
| {"hasNext"} could be set to {false} and make this the final incremental stream | ||
| update result. | ||
|
|
||
| ```json example | ||
| { | ||
| "incremental": [ | ||
| { | ||
| "id": "1", | ||
| "items": [{ "title": "Return of the Jedi" }] | ||
| } | ||
| ], | ||
| "hasNext": true | ||
| } | ||
| ``` | ||
|
|
||
| When the underlying iterator of the `films` field closes there is no more data | ||
| to deliver, so the third and final _incremental stream update result_ sets | ||
| {"hasNext"} to {false} to indicate the end of the _incremental stream_. | ||
|
|
||
| ```json example | ||
| { | ||
| "hasNext": false | ||
| } | ||
| ``` | ||
|
|
||
| ### Example 2 - 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 | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| In this example the response is an _incremental stream_ of the following | ||
| results. | ||
|
|
||
| The _initial incremental stream result_ contains the results of the `firstName` | ||
| field. Even though it is also present in the `HomeWorldFragment`, it must be | ||
| returned in the initial incremental stream result because it is also defined | ||
| outside of any fragments with the `@defer` directive. Additionally, there are | ||
| two _incremental pending notices_ indicating that results for both `@defer`s in | ||
| the query will be delivered in later _incremental stream update result_. | ||
|
|
||
| ```json example | ||
| { | ||
| "data": { | ||
| "person": { | ||
| "firstName": "Luke" | ||
| } | ||
| }, | ||
| "pending": [ | ||
| { "id": "0", "path": ["person"], "label": "homeWorldDefer" }, | ||
| { "id": "1", "path": ["person"], "label": "nameAndWorld" } | ||
| ], | ||
| "hasNext": true | ||
| } | ||
| ``` | ||
|
|
||
| In this example, the first _incremental stream update result_ contains the | ||
| deferred data from `HomeWorldFragment`. There is one _incremental completion | ||
| notice_, 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_. In this example, this incremental | ||
| result contains the id `"0"`, but since the `name` field was included in both | ||
| `HomeWorldFragment` and `NameAndHomeWorldFragment`, an id of `"1"` would also be | ||
| a valid response. | ||
|
|
||
| The second _incremental result_ in this _incremental stream update result_ | ||
| contains the data for the `terrain` field. This _incremental result_ contains a | ||
| {"subPath"} entry to indicate to clients that the _response position_ of this | ||
| result can be determined by concatenating: the path from the _incremental | ||
| pending notice_ for id `"0"`, and the value of this {"subPath"} entry. | ||
|
|
||
| ```json example | ||
| { | ||
| "incremental": [ | ||
| { | ||
| "id": "0", | ||
| "data": { "homeWorld": { "name": "Tatooine" } } | ||
| }, | ||
| { | ||
| "id": "0", | ||
| "subPath": ["homeWorld"], | ||
| "data": { "terrain": "desert" } | ||
| } | ||
| ], | ||
| "completed": [{ "id": "0" }], | ||
| "hasNext": true | ||
| } | ||
| ``` | ||
|
|
||
| The second _incremental stream update result_ contains the remaining data from | ||
| the `NameAndHomeWorldFragment`. `lastName` is the only remaining field from this | ||
| selection that has not been delivered in a previous result. With this field now | ||
| delivered, clients are informed that the `NameAndHomeWorldFragment` has been | ||
| completed by the presence of the associated _incremental completion notice_. | ||
| Additionally, {"hasNext"} is set to {false} indicating the end of the | ||
| _incremental stream_. | ||
|
|
||
| This example demonstrates that it is necessary for clients to process the entire | ||
| incremental stream, as both the initial data and previous incremental results | ||
| (with a potentially different value for {"id"}) may be required to complete a | ||
| deferred fragment. | ||
|
|
||
| ```json example | ||
| { | ||
| "incremental": [ | ||
| { | ||
| "id": "1", | ||
| "data": { "lastName": "Skywalker" } | ||
| } | ||
| ], | ||
| "completed": [{ "id": "1" }], | ||
| "hasNext": false | ||
| } | ||
| ``` | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel like the weirdness of this needs more calling out - name belongs to both id:0 and id:1, so we're kind of using id:0 as a shortcut (but id:1 would also work). We should make it clear to a client that it can't form
HomeWorldFragmentfrom solely the root data plus theid:0stuff andNameAndHomeWorldFragmentfrom solely the root data and theid:1stuff - they must merge the trees before extracting the data.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a paragraph here: https://github.com/graphql/graphql-spec/pull/1203/changes#diff-0237c26b29c484df8d43b13dc3a3658867cfd015d8ccea8ed93454c466dbacb3R200-R203