From 644df1d883e70eb71454524399f4798e32768ee5 Mon Sep 17 00:00:00 2001 From: Rob Richard Date: Tue, 7 Jan 2025 13:53:10 -0500 Subject: [PATCH 01/14] Spec edits for incremental delivery, Examples --- spec/Appendix E -- Examples.md | 191 +++++++++++++++++++++++++++++++++ spec/GraphQL.md | 2 + 2 files changed, 193 insertions(+) create mode 100644 spec/Appendix E -- Examples.md diff --git a/spec/Appendix E -- Examples.md b/spec/Appendix E -- Examples.md new file mode 100644 index 000000000..a63da30ad --- /dev/null +++ b/spec/Appendix E -- Examples.md @@ -0,0 +1,191 @@ +# 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 incremental stream might look like: + +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 responses will be delivered. There are two Pending Results indicating +that results for both the `@defer` and `@stream` in the query will be delivered +in the subsequent responses. + +```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 +} +``` + +Subsequent response 1, 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 +} +``` + +Subsequent response 2, 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} 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 +} +``` + +Subsequent response 3, contains no incremental data. {hasNext} set to {false} +indicates the end of the incremental stream. This response is sent when the +underlying iterator of the `films` field closes. + +```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 + } +} +``` + +The incremental stream might look like: + +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 response because it is also defined outside of any fragments with the +`@defer` directive. Additionally, There are two Pending Results indicating that +results for both `@defer`s in the query will be delivered in the subsequent +responses. + +```json example +{ + "data": { + "person": { + "firstName": "Luke" + } + }, + "pending": [ + { "id": "0", "path": ["person"], "label": "homeWorldDefer" }, + { "id": "1", "path": ["person"], "label": "nameAndWorld" } + ], + "hasNext": true +} +``` + +Subsequent response 1, 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 +} +``` + +Subsequent response 2, contains the remaining data from the +`NameAndHomeWorldFragment`. `lastName` is the only remaining field that has not +been delivered in a previous response. + +```json example +{ + "incremental": [ + { + "id": "1", + "data": { "lastName": "Skywalker" } + } + ], + "completed": [{ "id": "1" }], + "hasNext": false +} +``` diff --git a/spec/GraphQL.md b/spec/GraphQL.md index 7ac0717ac..0dbdfcdb0 100644 --- a/spec/GraphQL.md +++ b/spec/GraphQL.md @@ -60,4 +60,6 @@ working draft release can be found at # [Appendix: Specified Definitions](Appendix%20D%20--%20Specified%20Definitions.md) +# [Appendix: Examples](Appendix%20E%20--%20Examples.md) + # [Appendix: Licensing](../LICENSE.md) From c54049c56b4fe97841abccd46f9e1e140d8f54a7 Mon Sep 17 00:00:00 2001 From: Rob Richard Date: Fri, 31 Oct 2025 13:59:46 -0400 Subject: [PATCH 02/14] Spec edits for incremental delivery, Section 7 only --- spec/Appendix E -- Examples.md | 60 ++++----- spec/Section 7 -- Response.md | 233 ++++++++++++++++++++++++++++++++- 2 files changed, 257 insertions(+), 36 deletions(-) diff --git a/spec/Appendix E -- Examples.md b/spec/Appendix E -- Examples.md index a63da30ad..b9fe0161c 100644 --- a/spec/Appendix E -- Examples.md +++ b/spec/Appendix E -- Examples.md @@ -21,13 +21,13 @@ fragment HomeWorldFragment on Person { } ``` -The incremental stream might look like: +The _incremental stream_ might look like: -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 responses will be delivered. There are two Pending Results indicating -that results for both the `@defer` and `@stream` in the query will be delivered -in the subsequent responses. +The _initial execution result_ does not contain any deferred or streamed results +in the {"data"} entry. The initial execution result contains a {"hasNext"} +entry, indicating that _execution update result_ will be delivered. There are +two _pending result_ indicating that results for both the `@defer` and `@stream` +in the query will be delivered in the execution update results. ```json example { @@ -45,9 +45,9 @@ in the subsequent responses. } ``` -Subsequent response 1, contains the deferred data and the first streamed list -item. There is one Completed Result, indicating that the deferred data has been -completely delivered. +_Execution update result_ 1 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 { @@ -68,10 +68,10 @@ completely delivered. } ``` -Subsequent response 2, 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} would be set to {false} and -this would be the final response. +_Execution update result_ 2 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"} would be set to +{false} and this would be the final execution update result. ```json example { @@ -85,9 +85,9 @@ this would be the final response. } ``` -Subsequent response 3, contains no incremental data. {hasNext} set to {false} -indicates the end of the incremental stream. This response is sent when the -underlying iterator of the `films` field closes. +_Execution update result_ 3 contains no incremental data. {"hasNext"} set to +{false} indicates the end of the _incremental stream_. This response is sent +when the underlying iterator of the `films` field closes. ```json example { @@ -121,14 +121,14 @@ fragment NameAndHomeWorldFragment on Person { } ``` -The incremental stream might look like: +The _incremental stream_ might look like: -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 response because it is also defined outside of any fragments with the -`@defer` directive. Additionally, There are two Pending Results indicating that -results for both `@defer`s in the query will be delivered in the subsequent -responses. +The _initial execution result_ contains the results of the `firstName` field. +Even though it is also present in the `HomeWorldFragment`, it must be returned +in the initial response because it is also defined outside of any fragments with +the `@defer` directive. Additionally, There are two _pending result_ indicating +that results for both `@defer`s in the query will be delivered in the execution +update results. ```json example { @@ -145,15 +145,15 @@ responses. } ``` -Subsequent response 1, contains the deferred data from `HomeWorldFragment`. +_Execution update result_ 1 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. +`@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. +The second _incremental result_ contains the data for the `terrain` field. This +_incremental result_ contains a {"subPath"} entry 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 { @@ -173,7 +173,7 @@ Result with id `"0"` and this `subPath` entry. } ``` -Subsequent response 2, contains the remaining data from the +_Execution update result_ 2 contains the remaining data from the `NameAndHomeWorldFragment`. `lastName` is the only remaining field that has not been delivered in a previous response. diff --git a/spec/Section 7 -- Response.md b/spec/Section 7 -- Response.md index 4ece8639d..dddad1209 100644 --- a/spec/Section 7 -- Response.md +++ b/spec/Section 7 -- Response.md @@ -10,7 +10,8 @@ the case that any _execution error_ was raised and replaced with {null}. ## Response Format :: A GraphQL request returns a _response_. A _response_ is either an _execution -result_, a _response stream_, or a _request error result_. +result_, a _response stream_, an _incremental stream_, or a _request error +result_. ### Execution Result @@ -43,6 +44,14 @@ value of this entry is described in the "Extensions" section. subscription and the request included execution. A response stream must be a stream of _execution result_. +### Incremental Stream + +:: A GraphQL request returns an _incremental stream_ when the GraphQL service +has deferred or streamed data as a result of the `@defer` or `@stream` +directives. When the result of the GraphQL operation is an incremental stream, +the first value will be an _initial execution result_, followed by one or more +_execution update result_. + ### Request Error Result :: A GraphQL request returns a _request error result_ when one or more _request @@ -70,6 +79,59 @@ The _request error result_ map must not contain an entry with key {"data"}. The _request error result_ map may also contain an entry with key `extensions`. The value of this entry is described in the "Extensions" section. +### Initial Execution Result + +:: An _initial execution result_ is the first value yielded by an _incremental +stream_. + +An _initial execution result_ must be a map. + +The _initial execution result_ must contain an entry with key {"data"}, and may +contain entries with keys {"errors"} and {"extensions"}. The value of these +entries are defined in the same way as an _execution result_ as described in the +"Data", "Errors", and "Extensions" sections below. + +The _initial execution result_ must contain an entry with the key {"hasNext"}. +The value of this entry must be {true}. + +The _initial execution result_ may contain an entry with the key {"pending"}. +The value of this entry must be a non-empty list of _pending result_. Each +_pending result_ must be a map as described in the "Pending Result" section +below. + +The _initial execution result_ may contain an entry with they key +{"incremental"}. The value of this entry must be a non-empty list of +_incremental result_. Each _incremental result_ must be a map as described in +the "Incremental Result" section below. + +The _initial execution result_ may contain an entry with they key {"completed"}. +The value of this entry must be a non-empty list of _completed result_. Each +_completed result_ must be a map as described in the "Completed Result" section +below. + +### Execution Update Result + +:: An _execution update result_ is the value yielded by an _incremental stream_ +for all values except the first. + +An _execution update result_ must be a map. + +Unlike the _initial execution result_, an _execution update result_ must not +contain entries with keys {"data"} or {"errors"}. + +An _execution update result_ may contain an entry with the key {"extensions"}. +The value of this entry is described in the "Extensions" section. + +An _execution update result_ must contain an entry with the key {"hasNext"}. The +value of this entry must be {true} for all but the last response in the +_incremental stream_. The value of this entry must be {false} for the last +response of the incremental stream. + +The _initial execution result_ may contain entries with keys {"pending"}, +{"incremental"}, and/or {"completed"}. The value of these entries are defined in +the same way as an _initial execution result_ as described in the "Pending +Result", "Incremental Result", and "Completed Result" sections below. + ### Response Position @@ -94,6 +156,9 @@ represents a path in the response, not in the request. When a _response path_ is present on an _error result_, it identifies the _response position_ which raised the error. +When a _response path_ is present on an _incremental result_, it identifies the +_response position_ of the incremental data update. + A single field execution may result in multiple response positions. For example, ```graphql example @@ -323,15 +388,171 @@ discouraged. ### Extensions -The {"extensions"} entry in an _execution result_ or _request error result_, 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. +The {"extensions"} entry in an _execution result_, _request error result_, +_initial execution result_, or a _execution update result_, 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. + +### Pending Result + +:: A _pending result_ is used to communicate to clients that the GraphQL service +has chosen to incrementally deliver data associated with a `@defer` or `@stream` +directive. Each pending result corresponds to a specific `@defer` or `@stream` +directive located at a _response position_ in the response data. The presence of +a pending result indicates that clients should expect the associated data in +either the current response, or one of the following responses. + +**Pending Result Format** + +A _pending result_ must be a map. + +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 +_incremental result_ and _completed result_. The {"id"} value must be unique for +the entire _incremental stream_ response. There must not be any other pending +result in the _incremental stream_ 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 list +at this _response position_ is not known to be complete. Clients should expect +the GraphQL Service to incrementally deliver the remainder list items of this +list. 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 GraphQL Service to incrementally +deliver the remainder of the fields contained in the deferred fragment at this +_response position_. + +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. Clients should use this entry to differentiate the _pending results_ +for different deferred fragments at the same _response position_. + +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 execution result_, or one of the prior _execution update result_ in the +_incremental stream_. + +:: The _associated pending result_ is a specific _pending result_ associated +with any given _incremental result_ or _completed result_. The associated +pending result can be determined by finding the pending result where the value +of its {"id"} entry is the same value of the {"id"} entry of the given +incremental result or completed result. The associated pending result must +appear in the _incremental stream_, in the same or prior _initial execution +result_ or _execution update result_ as the given incremental result or +completed result. + +### 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 result_ must be a map. + +Every _incremental result_ must contain an entry with the key {"id"} with a +string value. The definition of _associated pending result_ describes how this +value is used to determine the associated pending result for a given +_incremental result_. + +#### Incremental List Result + +:: An _incremental list result_ is a _incremental result_ used to deliver +additional list items for a list field with a `@stream` directive. The +_associated pending result_ for this _incremental list result_ must be +associated with a `@stream` directive. + +The _response position_ for an _incremental list result_ is the {"path"} entry +from its _associated pending result_. + +**Incremental List Result Format** + +Every _incremental list result_ must contain an entry with the key {"id"}, used +to determine the _associated pending result_ for this _incremental result_. + +Every _incremental list result_ must contain an {"items"} entry. The {"items"} +entry must contain a list of additional list items for the list field in the +incremental list result's _response position_. The value of this entry must be a +list of the same type of the response field at this _response position_. + +If any _execution error_ were raised during the execution of the results in +{"items"} and these errors propagate to a _response position_ higher than the +_incremental list result_'s response position, The incremental list result is +considered failed and should not be included in the _incremental stream_. The +errors that caused this failure will be included in a _completed result_. + +If any _execution error_ were raised during the execution of the results in +{"items"} and these errors did not propagate to a path higher than the +_incremental list result_'s path, the incremental list result must contain an +entry with key {"errors"} containing these execution errors. The value of this +entry is described in the "Errors" section. + +#### Incremental Object Result + +:: An _incremental object result_ is a _incremental result_ used to deliver +additional response fields that were contained in one or more fragments with a +`@defer` directive. The _associated pending result_ for this _incremental object +result_ must be associated with a `@defer` directive. + +**Incremental Object Result Format** + +The _incremental object result_ may contain a {"subPath"} entry. If this entry +is present, The incremental object result's _response position_ can be +determined by concatenating the value of the _associated pending result_'s +{"path"} entry with the value of this {"subPath"} entry. If no {"subPath"} entry +is present, the _response position_ is the value of the associated pending +result's {"path"} entry. + +An _incremental object result_ may be used to deliver data for response fields +that were contained in more than one deferred fragments. In that case, the +_associated pending result_ of the incremental object result must be a _pending +result_ with the longest {"path"}. + +Every _incremental object result_ must contain a {"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 the field at the incremental +object result's _response position_. + +If any _execution error_ were raised during the execution of the results in +{"data"} and these errors propagated to a _response position_ higher than the +_incremental object result_'s response position, The incremental object result +is considered failed and should not be included in the incremental stream. The +errors that caused this failure will be included in a _completed result_. + +If any _execution error_ were raised during the execution of the results in +{"data"} and these errors did not propagate to a _response position_ higher than +the _incremental object result_'s response position, the incremental object +result must contain an entry with key {"errors"} containing these execution +errors. The value of this entry is described in the "Errors" section. + +### Completed Result + +:: A _completed result_ is used to communicate that the GraphQL service has +completed the incremental delivery of the data associated with the _associated +pending result_. The corresponding data must have been completed in the same +_initial execution result_ or _execution update result_ in which this completed +result appears. + +**Completed Result Format** + +A _completed result_ must be a map. + +Every _completed result_ must contain an entry with the key {"id"} with a string +value. The definition of _associated pending result_ describes how this value is +used to determine the associated pending result for a given _completed result_. + +A _completed result_ may contain an {"errors"} entry. When the {"errors"} entry +is present, it informs clients that the delivery of the data from the +_associated pending result_ has failed, due to an execution error propagating to +a _response position_ higher than the _incremental result_'s response position. +The {"errors"} entry must contain these execution errors. The value of this +entry is described in the "Errors" section. ### Additional Entries To ensure future changes to the protocol do not break existing services and -clients, the _execution result_ and _request error result_ maps must not contain +clients, any of the maps described in the "Response" section must not contain any entries other than those described above. Clients must ignore any entries other than those described above. From ff9c30cf75e1686d7e7e01e2e5088cfb4516dc4b Mon Sep 17 00:00:00 2001 From: Rob Richard Date: Thu, 6 Nov 2025 15:05:43 -0500 Subject: [PATCH 03/14] fix typos --- spec/Appendix E -- Examples.md | 2 +- spec/Section 7 -- Response.md | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/spec/Appendix E -- Examples.md b/spec/Appendix E -- Examples.md index b9fe0161c..618c95109 100644 --- a/spec/Appendix E -- Examples.md +++ b/spec/Appendix E -- Examples.md @@ -126,7 +126,7 @@ The _incremental stream_ might look like: The _initial execution result_ contains the results of the `firstName` field. Even though it is also present in the `HomeWorldFragment`, it must be returned in the initial response because it is also defined outside of any fragments with -the `@defer` directive. Additionally, There are two _pending result_ indicating +the `@defer` directive. Additionally, there are two _pending result_ indicating that results for both `@defer`s in the query will be delivered in the execution update results. diff --git a/spec/Section 7 -- Response.md b/spec/Section 7 -- Response.md index dddad1209..6668f932b 100644 --- a/spec/Section 7 -- Response.md +++ b/spec/Section 7 -- Response.md @@ -389,7 +389,7 @@ discouraged. ### Extensions The {"extensions"} entry in an _execution result_, _request error result_, -_initial execution result_, or a _execution update result_, if set, must have a +_initial execution result_, or an _execution update result_, 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. @@ -446,7 +446,7 @@ completed result. ### 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 +chosen to incrementally deliver. An incremental result may be either an _incremental list result_ or an _incremental object result_. An _incremental result_ must be a map. @@ -458,7 +458,7 @@ _incremental result_. #### Incremental List Result -:: An _incremental list result_ is a _incremental result_ used to deliver +:: An _incremental list result_ is an _incremental result_ used to deliver additional list items for a list field with a `@stream` directive. The _associated pending result_ for this _incremental list result_ must be associated with a `@stream` directive. @@ -478,7 +478,7 @@ list of the same type of the response field at this _response position_. If any _execution error_ were raised during the execution of the results in {"items"} and these errors propagate to a _response position_ higher than the -_incremental list result_'s response position, The incremental list result is +_incremental list result_'s response position, the incremental list result is considered failed and should not be included in the _incremental stream_. The errors that caused this failure will be included in a _completed result_. @@ -490,7 +490,7 @@ entry is described in the "Errors" section. #### Incremental Object Result -:: An _incremental object result_ is a _incremental result_ used to deliver +:: An _incremental object result_ is an _incremental result_ used to deliver additional response fields that were contained in one or more fragments with a `@defer` directive. The _associated pending result_ for this _incremental object result_ must be associated with a `@defer` directive. @@ -498,7 +498,7 @@ result_ must be associated with a `@defer` directive. **Incremental Object Result Format** The _incremental object result_ may contain a {"subPath"} entry. If this entry -is present, The incremental object result's _response position_ can be +is present, the incremental object result's _response position_ can be determined by concatenating the value of the _associated pending result_'s {"path"} entry with the value of this {"subPath"} entry. If no {"subPath"} entry is present, the _response position_ is the value of the associated pending @@ -516,7 +516,7 @@ object result's _response position_. If any _execution error_ were raised during the execution of the results in {"data"} and these errors propagated to a _response position_ higher than the -_incremental object result_'s response position, The incremental object result +_incremental object result_'s response position, the incremental object result is considered failed and should not be included in the incremental stream. The errors that caused this failure will be included in a _completed result_. From 554bf1b8a448eb3810cf42325e5a2e79774f4d54 Mon Sep 17 00:00:00 2001 From: Rob Richard Date: Thu, 4 Dec 2025 14:44:30 -0500 Subject: [PATCH 04/14] Update to address feedback in exampled appendix --- spec/Appendix E -- Examples.md | 80 ++++++++++++++++++++-------------- 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/spec/Appendix E -- Examples.md b/spec/Appendix E -- Examples.md index 618c95109..23a370390 100644 --- a/spec/Appendix E -- Examples.md +++ b/spec/Appendix E -- Examples.md @@ -21,13 +21,21 @@ fragment HomeWorldFragment on Person { } ``` -The _incremental stream_ might look like: +The response to this request will be an _incremental stream_ consisting of an +_initial execution result_ followed by one or more _execution update result_. -The _initial execution result_ does not contain any deferred or streamed results -in the {"data"} entry. The initial execution result contains a {"hasNext"} -entry, indicating that _execution update result_ will be delivered. There are -two _pending result_ indicating that results for both the `@defer` and `@stream` -in the query will be delivered in the execution update results. +The _initial execution result_ has: + +- a {"data"} entry containing the results of the GraphQL operation except for + the `@defer` and `@stream` selections; +- a {"pending"} entry containing two _pending result_, one for the `@defer` + selection and for the the `@stream` selection, indicating that these results + will be delivered in a later _execution 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 {"error"} entry; but not in +this example. ```json example { @@ -45,9 +53,11 @@ in the query will be delivered in the execution update results. } ``` -_Execution update result_ 1 contains the deferred data and the first streamed -list item. There is one _completed result_, indicating that the deferred data -has been completely delivered. +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 _execution update result_ 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 { @@ -68,10 +78,10 @@ has been completely delivered. } ``` -_Execution update result_ 2 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"} would be set to -{false} and this would be the final execution update result. +The second _execution 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"} would be +set to {false} and this would be the final execution update result. ```json example { @@ -85,9 +95,9 @@ the underlying iterator does not close synchronously so {"hasNext"} is set to } ``` -_Execution update result_ 3 contains no incremental data. {"hasNext"} set to -{false} indicates the end of the _incremental stream_. This response is sent -when the underlying iterator of the `films` field closes. +The third _execution update result_ contains no incremental data. {"hasNext"} +set to {false} indicates the end of the _incremental stream_. This execution +update result is sent when the underlying iterator of the `films` field closes. ```json example { @@ -121,14 +131,15 @@ fragment NameAndHomeWorldFragment on Person { } ``` -The _incremental stream_ might look like: +In this example the response is an _incremental stream_ of the following +results. The _initial execution result_ contains the results of the `firstName` field. Even though it is also present in the `HomeWorldFragment`, it must be returned -in the initial response because it is also defined outside of any fragments with -the `@defer` directive. Additionally, there are two _pending result_ indicating -that results for both `@defer`s in the query will be delivered in the execution -update results. +in the initial execution result because it is also defined outside of any +fragments with the `@defer` directive. Additionally, there are two _pending +result_ indicating that results for both `@defer`s in the query will be +delivered in later _execution update result_. ```json example { @@ -145,15 +156,17 @@ update results. } ``` -_Execution update result_ 1 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_. +In this example, the first _execution update result_ 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"} entry 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. +The second _incremental result_ in this _execution 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 _pending result_ with id `"0"` and +the value of this {"subPath"} entry. ```json example { @@ -173,9 +186,12 @@ _pending result_ with id `"0"` and this {"subPath"} entry. } ``` -_Execution update result_ 2 contains the remaining data from the -`NameAndHomeWorldFragment`. `lastName` is the only remaining field that has not -been delivered in a previous response. +The second _execution 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 _completed result_. Additionally, +{"hasNext"} is set to {false} indicating the end of the _incremental stream_. ```json example { From 00c726b5b323ef3b8d05c8b542d2d2e02fe58911 Mon Sep 17 00:00:00 2001 From: Rob Richard Date: Thu, 19 Feb 2026 14:33:57 -0500 Subject: [PATCH 05/14] Update terminology Initial Execution Result > Initial Incremental Stream Result Execution Update Result > Incremental Stream Update Result --- spec/Appendix E -- Examples.md | 65 ++++++++++----------- spec/Section 3 -- Type System.md | 11 ++-- spec/Section 7 -- Response.md | 96 ++++++++++++++++---------------- 3 files changed, 89 insertions(+), 83 deletions(-) diff --git a/spec/Appendix E -- Examples.md b/spec/Appendix E -- Examples.md index 23a370390..e742b9098 100644 --- a/spec/Appendix E -- Examples.md +++ b/spec/Appendix E -- Examples.md @@ -22,15 +22,16 @@ fragment HomeWorldFragment on Person { ``` The response to this request will be an _incremental stream_ consisting of an -_initial execution result_ followed by one or more _execution update result_. +_initial incremental stream result_ followed by one or more _incremental stream +update result_. -The _initial execution result_ has: +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 _pending result_, one for the `@defer` selection and for the the `@stream` selection, indicating that these results - will be delivered in a later _execution update result_; + 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. @@ -55,9 +56,9 @@ this example. 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 _execution update result_ contains the deferred data -and the first streamed list item. There is one _completed result_, indicating -that the deferred data has been completely delivered. +In this example, our first _incremental stream update result_ 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 { @@ -78,10 +79,11 @@ that the deferred data has been completely delivered. } ``` -The second _execution 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"} would be -set to {false} and this would be the final execution update result. +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"} would be set to {false} and this would be the final incremental +stream update result. ```json example { @@ -95,9 +97,10 @@ set to {false} and this would be the final execution update result. } ``` -The third _execution update result_ contains no incremental data. {"hasNext"} -set to {false} indicates the end of the _incremental stream_. This execution -update result is sent when the underlying iterator of the `films` field closes. +The third _incremental stream update result_ contains no incremental data. +{"hasNext"} set to {false} indicates the end of the _incremental stream_. This +incremental stream update result is sent when the underlying iterator of the +`films` field closes. ```json example { @@ -134,12 +137,12 @@ fragment NameAndHomeWorldFragment on Person { In this example the response is an _incremental stream_ of the following results. -The _initial execution result_ contains the results of the `firstName` field. -Even though it is also present in the `HomeWorldFragment`, it must be returned -in the initial execution result because it is also defined outside of any -fragments with the `@defer` directive. Additionally, there are two _pending -result_ indicating that results for both `@defer`s in the query will be -delivered in later _execution update result_. +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 _pending result_ indicating that results for both `@defer`s in the query +will be delivered in later _incremental stream update result_. ```json example { @@ -156,17 +159,17 @@ delivered in later _execution update result_. } ``` -In this example, the first _execution update result_ 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_. +In this example, the first _incremental stream update result_ 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_ in this _execution 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 _pending result_ with id `"0"` and -the value of this {"subPath"} entry. +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 _pending result_ +with id `"0"` and the value of this {"subPath"} entry. ```json example { @@ -186,8 +189,8 @@ the value of this {"subPath"} entry. } ``` -The second _execution update result_ contains the remaining data from the -`NameAndHomeWorldFragment`. `lastName` is the only remaining field from this +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 _completed result_. Additionally, diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index c49105661..925646b32 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -2343,8 +2343,9 @@ directive @defer( The `@defer` directive may be provided on a fragment spread or inline fragment to indicate that execution of the related selection set should be deferred. When a request includes the `@defer` directive, it may return an _incremental stream_ -consisting of an _initial execution result_ containing all non-deferred data, -followed by one or more _execution update result_ including deferred data. +consisting of an _initial incremental stream result_ containing all non-deferred +data, followed by one or more _incremental stream update result_ including +deferred data. The `@include` and `@skip` directives take precedence over `@defer`. @@ -2389,9 +2390,9 @@ directive @stream( The `@stream` directive may be provided for a field whose type incorporates a `List` type modifier. The directive enables returning a partial list initially, -followed by additional items in one or more _execution update result_. If the -field type incorporates multiple `List` type modifiers, only the outermost list -is streamed. +followed by additional items in one or more _incremental stream update result_. +If the field type incorporates multiple `List` type modifiers, only the +outermost list is streamed. Note: The mechanism through which items are streamed is implementation-defined and may use technologies such as asynchronous iterators. diff --git a/spec/Section 7 -- Response.md b/spec/Section 7 -- Response.md index 6668f932b..fa4ac6a1a 100644 --- a/spec/Section 7 -- Response.md +++ b/spec/Section 7 -- Response.md @@ -49,8 +49,8 @@ stream of _execution result_. :: A GraphQL request returns an _incremental stream_ when the GraphQL service has deferred or streamed data as a result of the `@defer` or `@stream` directives. When the result of the GraphQL operation is an incremental stream, -the first value will be an _initial execution result_, followed by one or more -_execution update result_. +the first value will be an _initial incremental stream result_, followed by one +or more _incremental stream update result_. ### Request Error Result @@ -79,58 +79,60 @@ The _request error result_ map must not contain an entry with key {"data"}. The _request error result_ map may also contain an entry with key `extensions`. The value of this entry is described in the "Extensions" section. -### Initial Execution Result +### Initial Incremental Stream Result -:: An _initial execution result_ is the first value yielded by an _incremental -stream_. +:: An _initial incremental stream result_ is the first value yielded by an +_incremental stream_. -An _initial execution result_ must be a map. +An _initial incremental stream result_ must be a map. -The _initial execution result_ must contain an entry with key {"data"}, and may -contain entries with keys {"errors"} and {"extensions"}. The value of these -entries are defined in the same way as an _execution result_ as described in the -"Data", "Errors", and "Extensions" sections below. +The _initial incremental stream result_ must contain an entry with key {"data"}, +and may contain entries with keys {"errors"} and {"extensions"}. The value of +these entries are defined in the same way as an _execution result_ as described +in the "Data", "Errors", and "Extensions" sections below. -The _initial execution result_ must contain an entry with the key {"hasNext"}. -The value of this entry must be {true}. +The _initial incremental stream result_ must contain an entry with the key +{"hasNext"}. The value of this entry must be {true}. -The _initial execution result_ may contain an entry with the key {"pending"}. -The value of this entry must be a non-empty list of _pending result_. Each -_pending result_ must be a map as described in the "Pending Result" section -below. +The _initial incremental stream result_ may contain an entry with the key +{"pending"}. The value of this entry must be a non-empty list of _pending +result_. Each _pending result_ must be a map as described in the "Pending +Result" section below. -The _initial execution result_ may contain an entry with they key +The _initial incremental stream result_ may contain an entry with they key {"incremental"}. The value of this entry must be a non-empty list of _incremental result_. Each _incremental result_ must be a map as described in the "Incremental Result" section below. -The _initial execution result_ may contain an entry with they key {"completed"}. -The value of this entry must be a non-empty list of _completed result_. Each -_completed result_ must be a map as described in the "Completed Result" section -below. +The _initial incremental stream result_ may contain an entry with they key +{"completed"}. The value of this entry must be a non-empty list of _completed +result_. Each _completed result_ must be a map as described in the "Completed +Result" section below. -### Execution Update Result +### Incremental Stream Update Result -:: An _execution update result_ is the value yielded by an _incremental stream_ -for all values except the first. +:: An _incremental stream update result_ is the value yielded by an _incremental +stream_ for all values except the first. -An _execution update result_ must be a map. +An _incremental stream update result_ must be a map. -Unlike the _initial execution result_, an _execution update result_ must not -contain entries with keys {"data"} or {"errors"}. +Unlike the _initial incremental stream result_, an _incremental stream update +result_ must not contain entries with keys {"data"} or {"errors"}. -An _execution update result_ may contain an entry with the key {"extensions"}. -The value of this entry is described in the "Extensions" section. +An _incremental stream update result_ may contain an entry with the key +{"extensions"}. The value of this entry is described in the "Extensions" +section. -An _execution update result_ must contain an entry with the key {"hasNext"}. The -value of this entry must be {true} for all but the last response in the -_incremental stream_. The value of this entry must be {false} for the last -response of the incremental stream. +An _incremental stream update result_ must contain an entry with the key +{"hasNext"}. The value of this entry must be {true} for all but the last +response in the _incremental stream_. The value of this entry must be {false} +for the last response of the incremental stream. -The _initial execution result_ may contain entries with keys {"pending"}, -{"incremental"}, and/or {"completed"}. The value of these entries are defined in -the same way as an _initial execution result_ as described in the "Pending -Result", "Incremental Result", and "Completed Result" sections below. +The _initial incremental stream result_ may contain entries with keys +{"pending"}, {"incremental"}, and/or {"completed"}. The value of these entries +are defined in the same way as an _initial incremental stream result_ as +described in the "Pending Result", "Incremental Result", and "Completed Result" +sections below. ### Response Position @@ -389,10 +391,10 @@ discouraged. ### Extensions The {"extensions"} entry in an _execution result_, _request error result_, -_initial execution result_, or an _execution update result_, 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. +_initial incremental stream result_, or an _incremental stream update result_, +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. ### Pending Result @@ -431,16 +433,16 @@ for different deferred fragments at the same _response position_. 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 execution result_, or one of the prior _execution update result_ in the -_incremental stream_. +_initial incremental stream result_, or one of the prior _incremental stream +update result_ in the _incremental stream_. :: The _associated pending result_ is a specific _pending result_ associated with any given _incremental result_ or _completed result_. The associated pending result can be determined by finding the pending result where the value of its {"id"} entry is the same value of the {"id"} entry of the given incremental result or completed result. The associated pending result must -appear in the _incremental stream_, in the same or prior _initial execution -result_ or _execution update result_ as the given incremental result or +appear in the _incremental stream_, in the same or prior _initial incremental +stream result_ or _execution update result_ as the given incremental result or completed result. ### Incremental Result @@ -531,8 +533,8 @@ errors. The value of this entry is described in the "Errors" section. :: A _completed result_ is used to communicate that the GraphQL service has completed the incremental delivery of the data associated with the _associated pending result_. The corresponding data must have been completed in the same -_initial execution result_ or _execution update result_ in which this completed -result appears. +_initial incremental stream result_ or _incremental stream update result_ in +which this completed result appears. **Completed Result Format** From 048315e22e8a8b78da4948b44fc433436168a858 Mon Sep 17 00:00:00 2001 From: Rob Richard Date: Thu, 19 Feb 2026 14:45:25 -0500 Subject: [PATCH 06/14] PR feedback --- spec/Section 7 -- Response.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/spec/Section 7 -- Response.md b/spec/Section 7 -- Response.md index fa4ac6a1a..df96e3b64 100644 --- a/spec/Section 7 -- Response.md +++ b/spec/Section 7 -- Response.md @@ -82,7 +82,9 @@ The value of this entry is described in the "Extensions" section. ### Initial Incremental Stream Result :: An _initial incremental stream result_ is the first value yielded by an -_incremental stream_. +_incremental stream_. It contains the result of executing any non-deferred +selections, along with any errors that occurred during their execution and +details of future _incremental stream update result_ to be expected. An _initial incremental stream result_ must be a map. @@ -554,9 +556,9 @@ entry is described in the "Errors" section. ### Additional Entries To ensure future changes to the protocol do not break existing services and -clients, any of the maps described in the "Response" section must not contain -any entries other than those described above. Clients must ignore any entries -other than those described above. +clients, any of the maps described in the "Response" section (with the exception +of {"extensions"}) must not contain any entries other than those described +above. Clients must ignore any entries other than those described above. ## Serialization Format From 0a424c12f40dd9cbc993ce53d095518d8210b37b Mon Sep 17 00:00:00 2001 From: Rob Richard Date: Thu, 19 Feb 2026 14:43:29 -0500 Subject: [PATCH 07/14] allow hasNext: false on initial result --- spec/Section 7 -- Response.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/spec/Section 7 -- Response.md b/spec/Section 7 -- Response.md index df96e3b64..dfcfa2c9d 100644 --- a/spec/Section 7 -- Response.md +++ b/spec/Section 7 -- Response.md @@ -49,8 +49,8 @@ stream of _execution result_. :: A GraphQL request returns an _incremental stream_ when the GraphQL service has deferred or streamed data as a result of the `@defer` or `@stream` directives. When the result of the GraphQL operation is an incremental stream, -the first value will be an _initial incremental stream result_, followed by one -or more _incremental stream update result_. +the first value will be an _initial incremental stream result_, optionally +followed by one or more _incremental stream update result_. ### Request Error Result @@ -94,7 +94,10 @@ these entries are defined in the same way as an _execution result_ as described in the "Data", "Errors", and "Extensions" sections below. The _initial incremental stream result_ must contain an entry with the key -{"hasNext"}. The value of this entry must be {true}. +{"hasNext"}. The value of this entry must be {true} if there are any +_incremental stream update results_ in the _incremental stream_. The value of +this entry must be {false} if the initial incremental stream result is the last +response of the incremental stream. The _initial incremental stream result_ may contain an entry with the key {"pending"}. The value of this entry must be a non-empty list of _pending From 1904ef4db8dedc862d7575f2505649812edf971e Mon Sep 17 00:00:00 2001 From: Rob Richard Date: Thu, 19 Feb 2026 14:44:06 -0500 Subject: [PATCH 08/14] pending is must on initial stream result --- spec/Section 7 -- Response.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/Section 7 -- Response.md b/spec/Section 7 -- Response.md index dfcfa2c9d..731045907 100644 --- a/spec/Section 7 -- Response.md +++ b/spec/Section 7 -- Response.md @@ -99,7 +99,7 @@ _incremental stream update results_ in the _incremental stream_. The value of this entry must be {false} if the initial incremental stream result is the last response of the incremental stream. -The _initial incremental stream result_ may contain an entry with the key +The _initial incremental stream result_ must contain an entry with the key {"pending"}. The value of this entry must be a non-empty list of _pending result_. Each _pending result_ must be a map as described in the "Pending Result" section below. From 03242f0a361b8722f42f80fa234e8bb6cd64572f Mon Sep 17 00:00:00 2001 From: Rob Richard Date: Thu, 19 Feb 2026 14:49:17 -0500 Subject: [PATCH 09/14] value => payload when describing what is yielded by a stream --- spec/Section 7 -- Response.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/Section 7 -- Response.md b/spec/Section 7 -- Response.md index 731045907..841376370 100644 --- a/spec/Section 7 -- Response.md +++ b/spec/Section 7 -- Response.md @@ -49,7 +49,7 @@ stream of _execution result_. :: A GraphQL request returns an _incremental stream_ when the GraphQL service has deferred or streamed data as a result of the `@defer` or `@stream` directives. When the result of the GraphQL operation is an incremental stream, -the first value will be an _initial incremental stream result_, optionally +the first payload will be an _initial incremental stream result_, optionally followed by one or more _incremental stream update result_. ### Request Error Result @@ -81,7 +81,7 @@ The value of this entry is described in the "Extensions" section. ### Initial Incremental Stream Result -:: An _initial incremental stream result_ is the first value yielded by an +:: An _initial incremental stream result_ is the first payload yielded by an _incremental stream_. It contains the result of executing any non-deferred selections, along with any errors that occurred during their execution and details of future _incremental stream update result_ to be expected. @@ -116,8 +116,8 @@ Result" section below. ### Incremental Stream Update Result -:: An _incremental stream update result_ is the value yielded by an _incremental -stream_ for all values except the first. +:: An _incremental stream update result_ is the payload yielded by an +_incremental stream_ for all payloads except the first. An _incremental stream update result_ must be a map. From 68a01dee9d5db409c71ad39d5992e4a8a5726e1a Mon Sep 17 00:00:00 2001 From: Rob Richard Date: Thu, 19 Feb 2026 15:01:16 -0500 Subject: [PATCH 10/14] Update description of _initial incremental stream result_ & _incremental stream update result_ --- spec/Section 7 -- Response.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/spec/Section 7 -- Response.md b/spec/Section 7 -- Response.md index 841376370..c349a3582 100644 --- a/spec/Section 7 -- Response.md +++ b/spec/Section 7 -- Response.md @@ -81,10 +81,11 @@ The value of this entry is described in the "Extensions" section. ### Initial Incremental Stream Result -:: An _initial incremental stream result_ is the first payload yielded by an -_incremental stream_. It contains the result of executing any non-deferred -selections, along with any errors that occurred during their execution and -details of future _incremental stream update result_ to be expected. +:: An _initial incremental stream result_ contains the result of executing any +non-deferred selections, along with any errors that occurred during their +execution, as well as details of any future _incremental stream update result_ +to be expected. An initial incremental stream result must be the first payload +yielded by an _incremental stream_. An _initial incremental stream result_ must be a map. @@ -116,8 +117,11 @@ Result" section below. ### Incremental Stream Update Result -:: An _incremental stream update result_ is the payload yielded by an -_incremental stream_ for all payloads except the first. +:: An _incremental stream update result_ contains the result of executing any +deferred selections, along with any errors that occurred during their execution, +as well as details of any future _incremental stream update result_ to be +expected. All payloads yielded by an _incremental stream_, except the first, +must be incremental stream update results. An _incremental stream update result_ must be a map. From b9d8e8a149b0511c7cdfda193454400fc483b9d6 Mon Sep 17 00:00:00 2001 From: Rob Richard Date: Wed, 4 Mar 2026 11:30:30 -0500 Subject: [PATCH 11/14] fix incorrect term --- spec/Section 7 -- Response.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/Section 7 -- Response.md b/spec/Section 7 -- Response.md index c349a3582..a9de6153d 100644 --- a/spec/Section 7 -- Response.md +++ b/spec/Section 7 -- Response.md @@ -137,7 +137,7 @@ An _incremental stream update result_ must contain an entry with the key response in the _incremental stream_. The value of this entry must be {false} for the last response of the incremental stream. -The _initial incremental stream result_ may contain entries with keys +The _incremental stream update result_ may contain entries with keys {"pending"}, {"incremental"}, and/or {"completed"}. The value of these entries are defined in the same way as an _initial incremental stream result_ as described in the "Pending Result", "Incremental Result", and "Completed Result" From ffacdf4333e0d6ddd0334655a7615f7d8f48fb38 Mon Sep 17 00:00:00 2001 From: Rob Richard Date: Sat, 21 Mar 2026 17:36:00 -0400 Subject: [PATCH 12/14] PR Feedback --- spec/Appendix E -- Examples.md | 26 +++-- spec/Section 7 -- Response.md | 207 ++++++++++++++++++--------------- 2 files changed, 127 insertions(+), 106 deletions(-) diff --git a/spec/Appendix E -- Examples.md b/spec/Appendix E -- Examples.md index e742b9098..da090cd7b 100644 --- a/spec/Appendix E -- Examples.md +++ b/spec/Appendix E -- Examples.md @@ -35,7 +35,7 @@ The _initial incremental stream result_ has: - 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 {"error"} entry; but not in +If an error were to occur, it would also have an {"errors"} entry; but not in this example. ```json example @@ -82,8 +82,8 @@ indicating that the deferred data has been completely delivered. 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"} would be set to {false} and this would be the final incremental -stream update result. +{"hasNext"} could be set to {false} and make this the final incremental stream +update result. ```json example { @@ -97,10 +97,9 @@ stream update result. } ``` -The third _incremental stream update result_ contains no incremental data. -{"hasNext"} set to {false} indicates the end of the _incremental stream_. This -incremental stream update result is sent when the underlying iterator of the -`films` field closes. +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 { @@ -163,13 +162,15 @@ In this example, the first _incremental stream update result_ 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_. +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 _pending result_ -with id `"0"` and the value of this {"subPath"} entry. +result can be determined by concatenating: the path from the _pending result_ +for id `"0"`, and the value of this {"subPath"} entry. ```json example { @@ -196,6 +197,11 @@ delivered, clients are informed that the `NameAndHomeWorldFragment` has been completed by the presence of the associated _completed result_. 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": [ diff --git a/spec/Section 7 -- Response.md b/spec/Section 7 -- Response.md index a9de6153d..43ac64c12 100644 --- a/spec/Section 7 -- Response.md +++ b/spec/Section 7 -- Response.md @@ -89,32 +89,42 @@ yielded by an _incremental stream_. An _initial incremental stream result_ must be a map. -The _initial incremental stream result_ must contain an entry with key {"data"}, -and may contain entries with keys {"errors"} and {"extensions"}. The value of -these entries are defined in the same way as an _execution result_ as described -in the "Data", "Errors", and "Extensions" sections below. - -The _initial incremental stream result_ must contain an entry with the key -{"hasNext"}. The value of this entry must be {true} if there are any -_incremental stream update results_ in the _incremental stream_. The value of -this entry must be {false} if the initial incremental stream result is the last -response of the incremental stream. - -The _initial incremental stream result_ must contain an entry with the key -{"pending"}. The value of this entry must be a non-empty list of _pending -result_. Each _pending result_ must be a map as described in the "Pending -Result" section below. +The _initial incremental stream result_ must contain entries with keys {"data"}, +{"pending"}, and {"hasNext"}, and may contain entries with keys {"errors"}, +{"incremental"}, {"completed"}, and {"extensions"}. + +The value of {"data"}, {"errors"} and {"extensions"} are defined in the same way +as an _execution result_ as described in the "Data", "Errors", and "Extensions" +sections below. + +The value of {"hasNext"} must be {false} if the initial incremental stream +result is the last response of the incremental stream. Otherwise, {"hasNext"} +must be {true}. -The _initial incremental stream result_ may contain an entry with they key -{"incremental"}. The value of this entry must be a non-empty list of +The value of {"pending"} must be a non-empty list of _pending result_. Each +_pending result_ must be a map as described in the "Pending Result" section +below. + +The value of {"incremental"}, if present, must be a non-empty list of _incremental result_. Each _incremental result_ must be a map as described in the "Incremental Result" section below. -The _initial incremental stream result_ may contain an entry with they key -{"completed"}. The value of this entry must be a non-empty list of _completed +The value of {"completed"}, if present, must be a non-empty list of _completed result_. Each _completed result_ must be a map as described in the "Completed Result" section below. +Note: A GraphQL service is permitted to include incrementally delivered data in +the _initial incremental stream_. For example, A GraphQL middleware layer, such +as a caching CDN or proxy service, may wish to intercept and rewrite the +_incremental stream_ before delivering it to a client. This service may collect +some or all of the _pending result_, _incremental result_, and _completed +result_ from the entire _incremental stream_ of the upstream service, and +construct a new incremental stream containing a single payload: an _initial +incremental stream result_ containing the all of the intercepted pending +results, incremental results, and completed results, and the {"hasNext"} entry +set to false. This would allow the client to efficiently render the entire +result without having to process multiple payloads. + ### Incremental Stream Update Result :: An _incremental stream update result_ contains the result of executing any @@ -125,23 +135,22 @@ must be incremental stream update results. An _incremental stream update result_ must be a map. -Unlike the _initial incremental stream result_, an _incremental stream update -result_ must not contain entries with keys {"data"} or {"errors"}. +The _incremental stream update result_ must contain an entry with the key +{"hasNext"}, and may contain entries with the keys {"pending"}, {"incremental"}, +{"completed"}, and {"extensions"}. Unlike the _initial incremental stream +result_, an _incremental stream update result_ must not contain entries with +keys {"data"} or {"errors"}. -An _incremental stream update result_ may contain an entry with the key -{"extensions"}. The value of this entry is described in the "Extensions" -section. +The value of {"hasNext"} must be {true} for all but the last response in the +_incremental stream_. Otherwise, {"hasNext"} must be {true}. -An _incremental stream update result_ must contain an entry with the key -{"hasNext"}. The value of this entry must be {true} for all but the last -response in the _incremental stream_. The value of this entry must be {false} -for the last response of the incremental stream. +The value of {"pending"}, {"incremental"}, and/or {"completed"}, if present are +defined in the same way as an _initial incremental stream result_ as described +in the "Pending Result", "Incremental Result", and "Completed Result" sections +below. -The _incremental stream update result_ may contain entries with keys -{"pending"}, {"incremental"}, and/or {"completed"}. The value of these entries -are defined in the same way as an _initial incremental stream result_ as -described in the "Pending Result", "Incremental Result", and "Completed Result" -sections below. +The value of {"extensions"}, if present, is defined in the same way as an +_execution result_ as described in the "Extensions" section below. ### Response Position @@ -167,7 +176,7 @@ represents a path in the response, not in the request. When a _response path_ is present on an _error result_, it identifies the _response position_ which raised the error. -When a _response path_ is present on an _incremental result_, it identifies the +When a _response path_ is present on a _pending result_, it identifies the _response position_ of the incremental data update. A single field execution may result in multiple response positions. For example, @@ -418,21 +427,23 @@ either the current response, or one of the following responses. A _pending result_ must be a map. -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 -_incremental result_ and _completed result_. The {"id"} value must be unique for -the entire _incremental stream_ response. There must not be any other pending -result in the _incremental stream_ 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 list -at this _response position_ is not known to be complete. Clients should expect -the GraphQL Service to incrementally deliver the remainder list items of this -list. 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 GraphQL Service to incrementally -deliver the remainder of the fields contained in the deferred fragment at this -_response position_. +A _pending result_ must contain entries with the keys {"id"} and {"path"}, and +may contain an entry with key {"label"}. + +The value of {"id"} must be a string. This {"id"} should be used by clients to +correlate pending results with _incremental result_ and _completed result_. The +{"id"} value must be unique across the entire _incremental stream_ response. +There must not be any other pending result in the _incremental stream_ with the +same {"id"}. + +The value of {"path"} must be a _response position_. When the pending result is +associated with a `@stream` directive, it indicates the list at this _response +position_ is not known to be complete. Clients should expect the GraphQL Service +to incrementally deliver the remainder list items of this list. 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 GraphQL Service to incrementally deliver the remainder of the +fields contained in the deferred fragment at this _response position_. If the associated `@defer` or `@stream` directive contains a `label` argument, the pending result must contain an entry {"label"} with the value of this @@ -445,14 +456,9 @@ this data, and the data can be found either in the {"data"} entry in the _initial incremental stream result_, or one of the prior _incremental stream update result_ in the _incremental stream_. -:: The _associated pending result_ is a specific _pending result_ associated -with any given _incremental result_ or _completed result_. The associated -pending result can be determined by finding the pending result where the value -of its {"id"} entry is the same value of the {"id"} entry of the given -incremental result or completed result. The associated pending result must -appear in the _incremental stream_, in the same or prior _initial incremental -stream result_ or _execution update result_ as the given incremental result or -completed result. +:: The _associated pending result_ of an _incremental result_ or _completed +result_ is the _pending result_ whose {"id"} entry has the same value as the +{"id"} entry of the given incremental result or completed result. ### Incremental Result @@ -462,10 +468,11 @@ _incremental list result_ or an _incremental object result_. An _incremental result_ must be a map. -Every _incremental result_ must contain an entry with the key {"id"} with a -string value. The definition of _associated pending result_ describes how this -value is used to determine the associated pending result for a given -_incremental result_. +Every _incremental result_ must contain an entry with the key {"id"}, the value +of which is a string referencing its _associated pending result_. The associated +pending result must appear either in the _initial incremental stream result_, in +a prior _incremental stream update result_, or in the same _incremental stream +update result_ as the _incremental result_ that references it. #### Incremental List Result @@ -479,25 +486,25 @@ from its _associated pending result_. **Incremental List Result Format** -Every _incremental list result_ must contain an entry with the key {"id"}, used -to determine the _associated pending result_ for this _incremental result_. - Every _incremental list result_ must contain an {"items"} entry. The {"items"} entry must contain a list of additional list items for the list field in the incremental list result's _response position_. The value of this entry must be a list of the same type of the response field at this _response position_. If any _execution error_ were raised during the execution of the results in -{"items"} and these errors propagate to a _response position_ higher than the -_incremental list result_'s response position, the incremental list result is -considered failed and should not be included in the _incremental stream_. The -errors that caused this failure will be included in a _completed result_. +{"items"} and these errors propagate to the _response position_ of the +_incremental list result_ (i.e. the streamed list), or a parent response +position of the incremental list result's response position (i.e. a parent of +the streamed list), the incremental list result is considered failed and should +not be included in the _incremental stream_. The errors that caused this failure +will be included in a _completed result_. If any _execution error_ were raised during the execution of the results in -{"items"} and these errors did not propagate to a path higher than the -_incremental list result_'s path, the incremental list result must contain an -entry with key {"errors"} containing these execution errors. The value of this -entry is described in the "Errors" section. +{"items"} and no such error propagated to the _response position_ of the +_incremental list result_, or a parent response position of the incremental list +result's response position, the incremental list result must contain an entry +with key {"errors"} containing these execution errors. The value of this entry +is described in the "Errors" section. #### Incremental Object Result @@ -508,17 +515,21 @@ result_ must be associated with a `@defer` directive. **Incremental Object Result Format** -The _incremental object result_ may contain a {"subPath"} entry. If this entry -is present, the incremental object result's _response position_ can be -determined by concatenating the value of the _associated pending result_'s -{"path"} entry with the value of this {"subPath"} entry. If no {"subPath"} entry -is present, the _response position_ is the value of the associated pending -result's {"path"} entry. +The _incremental object result_ may contain a {"subPath"} entry. If such an +entry is present, the _response position_ of the incremental object result is +the result of appending the value of this {"subPath"} to the value of the +{"path"} entry of the _associated pending result_. If no {"subPath"} entry is +present, the _response position_ is the value of the associated pending result's +{"path"} entry. An _incremental object result_ may be used to deliver data for response fields -that were contained in more than one deferred fragments. In that case, the -_associated pending result_ of the incremental object result must be a _pending -result_ with the longest {"path"}. +that were contained in more than one deferred fragment. + +In that case, the _associated pending result_ of the incremental object result +must be one of the _pending result_ that corresponding to a fragment that +contained the delivered responsive fields. If any of these pending results have +a {"path"} of varying length, one of the pending results with the longest +{"path"} must be chosen to minimize the size of the {"subPath"}. Every _incremental object result_ must contain a {"data"} entry. The {"data"} entry must contain a map of additional response fields. The {"data"} entry in an @@ -526,16 +537,16 @@ incremental object result will be of the type of the field at the incremental object result's _response position_. If any _execution error_ were raised during the execution of the results in -{"data"} and these errors propagated to a _response position_ higher than the +{"data"} and these errors propagated to a parent _response position_ of the _incremental object result_'s response position, the incremental object result is considered failed and should not be included in the incremental stream. The -errors that caused this failure will be included in a _completed result_. +error that caused this failure will be included in a _completed result_. If any _execution error_ were raised during the execution of the results in -{"data"} and these errors did not propagate to a _response position_ higher than -the _incremental object result_'s response position, the incremental object -result must contain an entry with key {"errors"} containing these execution -errors. The value of this entry is described in the "Errors" section. +{"data"} and no such error propagated to a parent _response position_ of the +_incremental object result_'s response position, the incremental object result +must contain an entry with key {"errors"} containing these execution errors. The +value of this entry is described in the "Errors" section. ### Completed Result @@ -549,16 +560,20 @@ which this completed result appears. A _completed result_ must be a map. -Every _completed result_ must contain an entry with the key {"id"} with a string -value. The definition of _associated pending result_ describes how this value is -used to determine the associated pending result for a given _completed result_. +A _completed result_ must contain an entry with the key {"id"}, and may contain +an entry with the key {"errors"}. + +The value of {"id"} must be a string referencing its _associated pending +result_. The associated pending result must appear either in the _initial +incremental stream result_, in a prior _incremental stream update result_, or in +the same _incremental stream update result_ as the _completed result_ that +references it. -A _completed result_ may contain an {"errors"} entry. When the {"errors"} entry -is present, it informs clients that the delivery of the data from the -_associated pending result_ has failed, due to an execution error propagating to -a _response position_ higher than the _incremental result_'s response position. -The {"errors"} entry must contain these execution errors. The value of this -entry is described in the "Errors" section. +The value of {"errors"}, if present, informs clients that the delivery of the +data from the _associated pending result_ has failed, due to an execution error +propagating to a parent _response position_ of the _incremental result_'s +response position. The {"errors"} entry must contain these execution errors. The +value of this entry is described in the "Errors" section. ### Additional Entries From e928e0f52941e863acc9026b27027ff1740d13e9 Mon Sep 17 00:00:00 2001 From: Rob Richard Date: Mon, 23 Mar 2026 10:56:33 -0400 Subject: [PATCH 13/14] Pending Result -> Incremental Pending Notice --- spec/Appendix E -- Examples.md | 14 +-- spec/Section 3 -- Type System.md | 12 +-- spec/Section 7 -- Response.md | 164 ++++++++++++++++--------------- 3 files changed, 98 insertions(+), 92 deletions(-) diff --git a/spec/Appendix E -- Examples.md b/spec/Appendix E -- Examples.md index da090cd7b..319a76a68 100644 --- a/spec/Appendix E -- Examples.md +++ b/spec/Appendix E -- Examples.md @@ -29,9 +29,9 @@ 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 _pending result_, 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 {"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. @@ -140,8 +140,8 @@ 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 _pending result_ indicating that results for both `@defer`s in the query -will be delivered in later _incremental stream update result_. +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 { @@ -169,8 +169,8 @@ 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 _pending result_ -for id `"0"`, and the value of this {"subPath"} entry. +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 { diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 925646b32..15f0e4ad5 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -2372,9 +2372,9 @@ fragment someFragment on User { - `label: String` - An optional string literal used by GraphQL clients to identify data in the _incremental stream_ and associate it with the corresponding defer directive. If provided, the GraphQL service must include - this label in the corresponding _pending result_ within the _incremental - stream_. The `label` argument must be unique across all `@defer` and `@stream` - directives in the document. Variables are disallowed (via + this label in the corresponding _incremental pending notice_ within the + _incremental stream_. The `label` argument must be unique across all `@defer` + and `@stream` directives in the document. Variables are disallowed (via [Defer And Stream Directive Labels Are Unique](#sec-Defer-And-Stream-Directive-Labels-Are-Unique)) because their values may not be known during validation. @@ -2420,9 +2420,9 @@ query myQuery($shouldStream: Boolean! = true) { - `label: String` - An optional string literal used by GraphQL clients to identify data in the _incremental stream_ and associate it with the corresponding stream directive. If provided, the GraphQL service must include - this label in the corresponding _pending result_ within the _incremental - stream_. The `label` argument must be unique across all `@defer` and `@stream` - directives in the document. Variables are disallowed (via + this label in the corresponding _incremental pending notice_ within the + _incremental stream_. The `label` argument must be unique across all `@defer` + and `@stream` directives in the document. Variables are disallowed (via [Defer And Stream Directive Labels Are Unique](#sec-Defer-And-Stream-Directive-Labels-Are-Unique)) because their values may not be known during validation. - `initialCount: Int! = 0` - The number of list items to include initially when diff --git a/spec/Section 7 -- Response.md b/spec/Section 7 -- Response.md index 43ac64c12..d0ccf25af 100644 --- a/spec/Section 7 -- Response.md +++ b/spec/Section 7 -- Response.md @@ -101,9 +101,9 @@ The value of {"hasNext"} must be {false} if the initial incremental stream result is the last response of the incremental stream. Otherwise, {"hasNext"} must be {true}. -The value of {"pending"} must be a non-empty list of _pending result_. Each -_pending result_ must be a map as described in the "Pending Result" section -below. +The value of {"pending"} must be a non-empty list of _incremental pending +notice_. Each _incremental pending notice_ must be a map as described in the +"Incremental Pending Notice" section below. The value of {"incremental"}, if present, must be a non-empty list of _incremental result_. Each _incremental result_ must be a map as described in @@ -117,12 +117,12 @@ Note: A GraphQL service is permitted to include incrementally delivered data in the _initial incremental stream_. For example, A GraphQL middleware layer, such as a caching CDN or proxy service, may wish to intercept and rewrite the _incremental stream_ before delivering it to a client. This service may collect -some or all of the _pending result_, _incremental result_, and _completed -result_ from the entire _incremental stream_ of the upstream service, and -construct a new incremental stream containing a single payload: an _initial -incremental stream result_ containing the all of the intercepted pending -results, incremental results, and completed results, and the {"hasNext"} entry -set to false. This would allow the client to efficiently render the entire +some or all of the _incremental pending notice_, _incremental result_, and +_completed result_ from the entire _incremental stream_ of the upstream service, +and construct a new incremental stream containing a single payload: an _initial +incremental stream result_ containing the all of the intercepted incremental +pending notices, incremental results, and completed results, and the {"hasNext"} +entry set to false. This would allow the client to efficiently render the entire result without having to process multiple payloads. ### Incremental Stream Update Result @@ -146,8 +146,8 @@ _incremental stream_. Otherwise, {"hasNext"} must be {true}. The value of {"pending"}, {"incremental"}, and/or {"completed"}, if present are defined in the same way as an _initial incremental stream result_ as described -in the "Pending Result", "Incremental Result", and "Completed Result" sections -below. +in the "Incremental Pending Notice", "Incremental Result", and "Completed +Result" sections below. The value of {"extensions"}, if present, is defined in the same way as an _execution result_ as described in the "Extensions" section below. @@ -176,8 +176,8 @@ represents a path in the response, not in the request. When a _response path_ is present on an _error result_, it identifies the _response position_ which raised the error. -When a _response path_ is present on a _pending result_, it identifies the -_response position_ of the incremental data update. +When a _response path_ is present on an _incremental pending notice_, it +identifies the _response position_ of the incremental data update. A single field execution may result in multiple response positions. For example, @@ -414,51 +414,55 @@ 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. -### Pending Result +### Incremental Pending Notice -:: A _pending result_ is used to communicate to clients that the GraphQL service -has chosen to incrementally deliver data associated with a `@defer` or `@stream` -directive. Each pending result corresponds to a specific `@defer` or `@stream` -directive located at a _response position_ in the response data. The presence of -a pending result indicates that clients should expect the associated data in -either the current response, or one of the following responses. +:: A _incremental pending notice_ is used to communicate to clients that the +GraphQL service has chosen to incrementally deliver data associated with a +`@defer` or `@stream` directive. Each incremental pending notice corresponds to +a specific `@defer` or `@stream` directive located at a _response position_ in +the response data. The presence of an incremental pending notice indicates that +clients should expect the associated data in either the current response, or one +of the following responses. -**Pending Result Format** +**Incremental Pending Notice Format** -A _pending result_ must be a map. +An _incremental pending notice_ must be a map. -A _pending result_ must contain entries with the keys {"id"} and {"path"}, and -may contain an entry with key {"label"}. +An _incremental pending notice_ must contain entries with the keys {"id"} and +{"path"}, and may contain an entry with key {"label"}. The value of {"id"} must be a string. This {"id"} should be used by clients to -correlate pending results with _incremental result_ and _completed result_. The -{"id"} value must be unique across the entire _incremental stream_ response. -There must not be any other pending result in the _incremental stream_ with the -same {"id"}. - -The value of {"path"} must be a _response position_. When the pending result is -associated with a `@stream` directive, it indicates the list at this _response -position_ is not known to be complete. Clients should expect the GraphQL Service -to incrementally deliver the remainder list items of this list. 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 GraphQL Service to incrementally deliver the remainder of the -fields contained in the deferred fragment at this _response position_. +correlate incremental pending notices with _incremental result_ and _completed +result_. The {"id"} value must be unique across the entire _incremental stream_ +response. There must not be any other incremental pending notice in the +_incremental stream_ with the same {"id"}. + +The value of {"path"} must be a _response position_. When the incremental +pending notice is associated with a `@stream` directive, it indicates the list +at this _response position_ is not known to be complete. Clients should expect +the GraphQL Service to incrementally deliver the remainder list items of this +list. When the incremental pending notice 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 GraphQL Service +to incrementally deliver the remainder of the fields contained in the deferred +fragment at this _response position_. 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. Clients should use this entry to differentiate the _pending results_ -for different deferred fragments at the same _response position_. - -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 incremental stream result_, or one of the prior _incremental stream -update result_ in the _incremental stream_. - -:: The _associated pending result_ of an _incremental result_ or _completed -result_ is the _pending result_ whose {"id"} entry has the same value as the -{"id"} entry of the given incremental result or completed result. +the incremental pending notice must contain an entry {"label"} with the value of +this argument. Clients should use this entry to differentiate the _incremental +pending notices_ for different deferred fragments at the same _response +position_. + +If an incremental pending notice 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 incremental stream result_, or one of the prior +_incremental stream update result_ in the _incremental stream_. + +:: The _associated incremental pending notice_ of an _incremental result_ or +_completed result_ is the _incremental pending notice_ whose {"id"} entry has +the same value as the {"id"} entry of the given incremental result or completed +result. ### Incremental Result @@ -469,20 +473,21 @@ _incremental list result_ or an _incremental object result_. An _incremental result_ must be a map. Every _incremental result_ must contain an entry with the key {"id"}, the value -of which is a string referencing its _associated pending result_. The associated -pending result must appear either in the _initial incremental stream result_, in -a prior _incremental stream update result_, or in the same _incremental stream -update result_ as the _incremental result_ that references it. +of which is a string referencing its _associated incremental pending notice_. +The associated incremental pending notice must appear either in the _initial +incremental stream result_, in a prior _incremental stream update result_, or in +the same _incremental stream update result_ as the _incremental result_ that +references it. #### Incremental List Result :: An _incremental list result_ is an _incremental result_ used to deliver additional list items for a list field with a `@stream` directive. The -_associated pending result_ for this _incremental list result_ must be -associated with a `@stream` directive. +_associated incremental pending notice_ for this _incremental list result_ must +be associated with a `@stream` directive. The _response position_ for an _incremental list result_ is the {"path"} entry -from its _associated pending result_. +from its _associated incremental pending notice_. **Incremental List Result Format** @@ -510,26 +515,27 @@ is described in the "Errors" section. :: An _incremental object result_ is an _incremental result_ used to deliver additional response fields that were contained in one or more fragments with a -`@defer` directive. The _associated pending result_ for this _incremental object -result_ must be associated with a `@defer` directive. +`@defer` directive. The _associated incremental pending notice_ for this +_incremental object result_ must be associated with a `@defer` directive. **Incremental Object Result Format** The _incremental object result_ may contain a {"subPath"} entry. If such an entry is present, the _response position_ of the incremental object result is the result of appending the value of this {"subPath"} to the value of the -{"path"} entry of the _associated pending result_. If no {"subPath"} entry is -present, the _response position_ is the value of the associated pending result's -{"path"} entry. +{"path"} entry of the _associated incremental pending notice_. If no {"subPath"} +entry is present, the _response position_ is the value of the associated +incremental pending notice's {"path"} entry. An _incremental object result_ may be used to deliver data for response fields that were contained in more than one deferred fragment. -In that case, the _associated pending result_ of the incremental object result -must be one of the _pending result_ that corresponding to a fragment that -contained the delivered responsive fields. If any of these pending results have -a {"path"} of varying length, one of the pending results with the longest -{"path"} must be chosen to minimize the size of the {"subPath"}. +In that case, the _associated incremental pending notice_ of the incremental +object result must be one of the _incremental pending notice_ that corresponding +to a fragment that contained the delivered responsive fields. If any of these +incremental pending notices have a {"path"} of varying length, one of the +incremental pending notices with the longest {"path"} must be chosen to minimize +the size of the {"subPath"}. Every _incremental object result_ must contain a {"data"} entry. The {"data"} entry must contain a map of additional response fields. The {"data"} entry in an @@ -552,9 +558,9 @@ value of this entry is described in the "Errors" section. :: A _completed result_ is used to communicate that the GraphQL service has completed the incremental delivery of the data associated with the _associated -pending result_. The corresponding data must have been completed in the same -_initial incremental stream result_ or _incremental stream update result_ in -which this completed result appears. +incremental pending notice_. The corresponding data must have been completed in +the same _initial incremental stream result_ or _incremental stream update +result_ in which this completed result appears. **Completed Result Format** @@ -563,17 +569,17 @@ A _completed result_ must be a map. A _completed result_ must contain an entry with the key {"id"}, and may contain an entry with the key {"errors"}. -The value of {"id"} must be a string referencing its _associated pending -result_. The associated pending result must appear either in the _initial -incremental stream result_, in a prior _incremental stream update result_, or in -the same _incremental stream update result_ as the _completed result_ that -references it. +The value of {"id"} must be a string referencing its _associated incremental +pending notice_. The associated incremental pending notice must appear either in +the _initial incremental stream result_, in a prior _incremental stream update +result_, or in the same _incremental stream update result_ as the _completed +result_ that references it. The value of {"errors"}, if present, informs clients that the delivery of the -data from the _associated pending result_ has failed, due to an execution error -propagating to a parent _response position_ of the _incremental result_'s -response position. The {"errors"} entry must contain these execution errors. The -value of this entry is described in the "Errors" section. +data from the _associated incremental pending notice_ has failed, due to an +execution error propagating to a parent _response position_ of the _incremental +result_'s response position. The {"errors"} entry must contain these execution +errors. The value of this entry is described in the "Errors" section. ### Additional Entries From 711f708142b6b370f0c8c475771aaa58eca8449c Mon Sep 17 00:00:00 2001 From: Rob Richard Date: Mon, 23 Mar 2026 11:11:11 -0400 Subject: [PATCH 14/14] Completed Result -> Incremental Completion Notice --- spec/Appendix E -- Examples.md | 23 ++++++++------- spec/Section 7 -- Response.md | 54 ++++++++++++++++++---------------- 2 files changed, 41 insertions(+), 36 deletions(-) diff --git a/spec/Appendix E -- Examples.md b/spec/Appendix E -- Examples.md index 319a76a68..a2a1b4d2d 100644 --- a/spec/Appendix E -- Examples.md +++ b/spec/Appendix E -- Examples.md @@ -57,8 +57,9 @@ this example. 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 _completed result_, -indicating that the deferred data has been completely delivered. +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 { @@ -159,12 +160,13 @@ the query will be delivered in later _incremental stream update result_. ``` In this example, the first _incremental stream update result_ 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_. 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. +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 @@ -194,8 +196,9 @@ 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 _completed result_. Additionally, -{"hasNext"} is set to {false} indicating the end of the _incremental stream_. +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 diff --git a/spec/Section 7 -- Response.md b/spec/Section 7 -- Response.md index d0ccf25af..3bb90807f 100644 --- a/spec/Section 7 -- Response.md +++ b/spec/Section 7 -- Response.md @@ -109,21 +109,22 @@ The value of {"incremental"}, if present, must be a non-empty list of _incremental result_. Each _incremental result_ must be a map as described in the "Incremental Result" section below. -The value of {"completed"}, if present, must be a non-empty list of _completed -result_. Each _completed result_ must be a map as described in the "Completed -Result" section below. +The value of {"completed"}, if present, must be a non-empty list of _incremental +completion notice_. Each _incremental completion notice_ must be a map as +described in the "Incremental Completion Notice" section below. Note: A GraphQL service is permitted to include incrementally delivered data in the _initial incremental stream_. For example, A GraphQL middleware layer, such as a caching CDN or proxy service, may wish to intercept and rewrite the _incremental stream_ before delivering it to a client. This service may collect some or all of the _incremental pending notice_, _incremental result_, and -_completed result_ from the entire _incremental stream_ of the upstream service, -and construct a new incremental stream containing a single payload: an _initial -incremental stream result_ containing the all of the intercepted incremental -pending notices, incremental results, and completed results, and the {"hasNext"} -entry set to false. This would allow the client to efficiently render the entire -result without having to process multiple payloads. +_incremental completion notice_ from the entire _incremental stream_ of the +upstream service, and construct a new incremental stream containing a single +payload: an _initial incremental stream result_ containing the all of the +intercepted incremental pending notices, incremental results, and incremental +completion notices, and the {"hasNext"} entry set to false. This would allow the +client to efficiently render the entire result without having to process +multiple payloads. ### Incremental Stream Update Result @@ -146,8 +147,8 @@ _incremental stream_. Otherwise, {"hasNext"} must be {true}. The value of {"pending"}, {"incremental"}, and/or {"completed"}, if present are defined in the same way as an _initial incremental stream result_ as described -in the "Incremental Pending Notice", "Incremental Result", and "Completed -Result" sections below. +in the "Incremental Pending Notice", "Incremental Result", and "Incremental +Completion Notice" sections below. The value of {"extensions"}, if present, is defined in the same way as an _execution result_ as described in the "Extensions" section below. @@ -460,9 +461,9 @@ incrementally deliver this data, and the data can be found either in the _incremental stream update result_ in the _incremental stream_. :: The _associated incremental pending notice_ of an _incremental result_ or -_completed result_ is the _incremental pending notice_ whose {"id"} entry has -the same value as the {"id"} entry of the given incremental result or completed -result. +_incremental completion notice_ is the _incremental pending notice_ whose {"id"} +entry has the same value as the {"id"} entry of the given incremental result or +incremental completion notice. ### Incremental Result @@ -502,7 +503,7 @@ _incremental list result_ (i.e. the streamed list), or a parent response position of the incremental list result's response position (i.e. a parent of the streamed list), the incremental list result is considered failed and should not be included in the _incremental stream_. The errors that caused this failure -will be included in a _completed result_. +will be included in an _incremental completion notice_. If any _execution error_ were raised during the execution of the results in {"items"} and no such error propagated to the _response position_ of the @@ -546,7 +547,8 @@ If any _execution error_ were raised during the execution of the results in {"data"} and these errors propagated to a parent _response position_ of the _incremental object result_'s response position, the incremental object result is considered failed and should not be included in the incremental stream. The -error that caused this failure will be included in a _completed result_. +error that caused this failure will be included in an _incremental completion +notice_. If any _execution error_ were raised during the execution of the results in {"data"} and no such error propagated to a parent _response position_ of the @@ -554,20 +556,20 @@ _incremental object result_'s response position, the incremental object result must contain an entry with key {"errors"} containing these execution errors. The value of this entry is described in the "Errors" section. -### Completed Result +### Incremental Completion Notice -:: A _completed result_ is used to communicate that the GraphQL service has -completed the incremental delivery of the data associated with the _associated -incremental pending notice_. The corresponding data must have been completed in -the same _initial incremental stream result_ or _incremental stream update -result_ in which this completed result appears. +:: An _incremental completion notice_ is used to communicate that the GraphQL +service has completed the incremental delivery of the data associated with the +_associated incremental pending notice_. The corresponding data must have been +completed in the same _initial incremental stream result_ or _incremental stream +update result_ in which this incremental completion notice appears. -**Completed Result Format** +**Incremental Completion Notice Format** -A _completed result_ must be a map. +An _incremental completion notice_ must be a map. -A _completed result_ must contain an entry with the key {"id"}, and may contain -an entry with the key {"errors"}. +An _incremental completion notice_ must contain an entry with the key {"id"}, +and may contain an entry with the key {"errors"}. The value of {"id"} must be a string referencing its _associated incremental pending notice_. The associated incremental pending notice must appear either in