Skip to content

Commit 0486099

Browse files
authored
Spec edits for incremental delivery, Response Section and Examples Appendix (#1203)
* Spec edits for incremental delivery, Examples * Spec edits for incremental delivery, Section 7 only * fix typos * Update to address feedback in exampled appendix * Update terminology Initial Execution Result > Initial Incremental Stream Result Execution Update Result > Incremental Stream Update Result * PR feedback * allow hasNext: false on initial result * pending is must on initial stream result * value => payload when describing what is yielded by a stream * Update description of _initial incremental stream result_ & _incremental stream update result_ * fix incorrect term * PR Feedback * Pending Result -> Incremental Pending Notice * Completed Result -> Incremental Completion Notice
1 parent 87a05e3 commit 0486099

4 files changed

Lines changed: 494 additions & 17 deletions

File tree

spec/Appendix E -- Examples.md

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
# E. Appendix: Examples
2+
3+
## Incremental Delivery Examples
4+
5+
### Example 1 - A query containing both defer and stream
6+
7+
```graphql example
8+
query {
9+
person(id: "cGVvcGxlOjE=") {
10+
...HomeWorldFragment @defer(label: "homeWorldDefer")
11+
name
12+
films @stream(initialCount: 1, label: "filmsStream") {
13+
title
14+
}
15+
}
16+
}
17+
fragment HomeWorldFragment on Person {
18+
homeWorld {
19+
name
20+
}
21+
}
22+
```
23+
24+
The response to this request will be an _incremental stream_ consisting of an
25+
_initial incremental stream result_ followed by one or more _incremental stream
26+
update result_.
27+
28+
The _initial incremental stream result_ has:
29+
30+
- a {"data"} entry containing the results of the GraphQL operation except for
31+
the `@defer` and `@stream` selections;
32+
- a {"pending"} entry containing two _incremental pending notices_, one for the
33+
`@defer` selection and for the the `@stream` selection, indicating that these
34+
results will be delivered in a later _incremental stream update result_;
35+
- a {"hasNext"} entry with the value {true}, indicating that the response is not
36+
yet complete.
37+
38+
If an error were to occur, it would also have an {"errors"} entry; but not in
39+
this example.
40+
41+
```json example
42+
{
43+
"data": {
44+
"person": {
45+
"name": "Luke Skywalker",
46+
"films": [{ "title": "A New Hope" }]
47+
}
48+
},
49+
"pending": [
50+
{ "id": "0", "path": ["person"], "label": "homeWorldDefer" },
51+
{ "id": "1", "path": ["person", "films"], "label": "filmsStream" }
52+
],
53+
"hasNext": true
54+
}
55+
```
56+
57+
Depending on the behavior of the backend and the time at which the deferred and
58+
streamed resources resolve, the stream may produce results in different orders.
59+
In this example, our first _incremental stream update result_ contains the
60+
deferred data and the first streamed list item. There is one _incremental
61+
completion notice_, indicating that the deferred data has been completely
62+
delivered.
63+
64+
```json example
65+
{
66+
"incremental": [
67+
{
68+
"id": "0",
69+
"data": { "homeWorld": { "name": "Tatooine" } }
70+
},
71+
{
72+
"id": "1",
73+
"items": [{ "title": "The Empire Strikes Back" }]
74+
}
75+
],
76+
"completed": [
77+
{"id": "0"}
78+
]
79+
"hasNext": true
80+
}
81+
```
82+
83+
The second _incremental stream update result_ contains the final stream results.
84+
In this example, the underlying iterator does not close synchronously so
85+
{"hasNext"} is set to {true}. If this iterator did close synchronously,
86+
{"hasNext"} could be set to {false} and make this the final incremental stream
87+
update result.
88+
89+
```json example
90+
{
91+
"incremental": [
92+
{
93+
"id": "1",
94+
"items": [{ "title": "Return of the Jedi" }]
95+
}
96+
],
97+
"hasNext": true
98+
}
99+
```
100+
101+
When the underlying iterator of the `films` field closes there is no more data
102+
to deliver, so the third and final _incremental stream update result_ sets
103+
{"hasNext"} to {false} to indicate the end of the _incremental stream_.
104+
105+
```json example
106+
{
107+
"hasNext": false
108+
}
109+
```
110+
111+
### Example 2 - A query containing overlapping defers
112+
113+
```graphql example
114+
query {
115+
person(id: "cGVvcGxlOjE=") {
116+
...HomeWorldFragment @defer(label: "homeWorldDefer")
117+
...NameAndHomeWorldFragment @defer(label: "nameAndWorld")
118+
firstName
119+
}
120+
}
121+
fragment HomeWorldFragment on Person {
122+
homeWorld {
123+
name
124+
terrain
125+
}
126+
}
127+
128+
fragment NameAndHomeWorldFragment on Person {
129+
firstName
130+
lastName
131+
homeWorld {
132+
name
133+
}
134+
}
135+
```
136+
137+
In this example the response is an _incremental stream_ of the following
138+
results.
139+
140+
The _initial incremental stream result_ contains the results of the `firstName`
141+
field. Even though it is also present in the `HomeWorldFragment`, it must be
142+
returned in the initial incremental stream result because it is also defined
143+
outside of any fragments with the `@defer` directive. Additionally, there are
144+
two _incremental pending notices_ indicating that results for both `@defer`s in
145+
the query will be delivered in later _incremental stream update result_.
146+
147+
```json example
148+
{
149+
"data": {
150+
"person": {
151+
"firstName": "Luke"
152+
}
153+
},
154+
"pending": [
155+
{ "id": "0", "path": ["person"], "label": "homeWorldDefer" },
156+
{ "id": "1", "path": ["person"], "label": "nameAndWorld" }
157+
],
158+
"hasNext": true
159+
}
160+
```
161+
162+
In this example, the first _incremental stream update result_ contains the
163+
deferred data from `HomeWorldFragment`. There is one _incremental completion
164+
notice_, indicating that `HomeWorldFragment` has been completely delivered.
165+
Because the `homeWorld` field is present in two separate `@defer`s, it is
166+
separated into its own _incremental result_. In this example, this incremental
167+
result contains the id `"0"`, but since the `name` field was included in both
168+
`HomeWorldFragment` and `NameAndHomeWorldFragment`, an id of `"1"` would also be
169+
a valid response.
170+
171+
The second _incremental result_ in this _incremental stream update result_
172+
contains the data for the `terrain` field. This _incremental result_ contains a
173+
{"subPath"} entry to indicate to clients that the _response position_ of this
174+
result can be determined by concatenating: the path from the _incremental
175+
pending notice_ for id `"0"`, and the value of this {"subPath"} entry.
176+
177+
```json example
178+
{
179+
"incremental": [
180+
{
181+
"id": "0",
182+
"data": { "homeWorld": { "name": "Tatooine" } }
183+
},
184+
{
185+
"id": "0",
186+
"subPath": ["homeWorld"],
187+
"data": { "terrain": "desert" }
188+
}
189+
],
190+
"completed": [{ "id": "0" }],
191+
"hasNext": true
192+
}
193+
```
194+
195+
The second _incremental stream update result_ contains the remaining data from
196+
the `NameAndHomeWorldFragment`. `lastName` is the only remaining field from this
197+
selection that has not been delivered in a previous result. With this field now
198+
delivered, clients are informed that the `NameAndHomeWorldFragment` has been
199+
completed by the presence of the associated _incremental completion notice_.
200+
Additionally, {"hasNext"} is set to {false} indicating the end of the
201+
_incremental stream_.
202+
203+
This example demonstrates that it is necessary for clients to process the entire
204+
incremental stream, as both the initial data and previous incremental results
205+
(with a potentially different value for {"id"}) may be required to complete a
206+
deferred fragment.
207+
208+
```json example
209+
{
210+
"incremental": [
211+
{
212+
"id": "1",
213+
"data": { "lastName": "Skywalker" }
214+
}
215+
],
216+
"completed": [{ "id": "1" }],
217+
"hasNext": false
218+
}
219+
```

spec/GraphQL.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,6 @@ working draft release can be found at
6060

6161
# [Appendix: Specified Definitions](Appendix%20D%20--%20Specified%20Definitions.md)
6262

63+
# [Appendix: Examples](Appendix%20E%20--%20Examples.md)
64+
6365
# [Appendix: Licensing](../LICENSE.md)

spec/Section 3 -- Type System.md

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2343,8 +2343,9 @@ directive @defer(
23432343
The `@defer` directive may be provided on a fragment spread or inline fragment
23442344
to indicate that execution of the related selection set should be deferred. When
23452345
a request includes the `@defer` directive, it may return an _incremental stream_
2346-
consisting of an _initial execution result_ containing all non-deferred data,
2347-
followed by one or more _execution update result_ including deferred data.
2346+
consisting of an _initial incremental stream result_ containing all non-deferred
2347+
data, followed by one or more _incremental stream update result_ including
2348+
deferred data.
23482349

23492350
The `@include` and `@skip` directives take precedence over `@defer`.
23502351

@@ -2371,9 +2372,9 @@ fragment someFragment on User {
23712372
- `label: String` - An optional string literal used by GraphQL clients to
23722373
identify data in the _incremental stream_ and associate it with the
23732374
corresponding defer directive. If provided, the GraphQL service must include
2374-
this label in the corresponding _pending result_ within the _incremental
2375-
stream_. The `label` argument must be unique across all `@defer` and `@stream`
2376-
directives in the document. Variables are disallowed (via
2375+
this label in the corresponding _incremental pending notice_ within the
2376+
_incremental stream_. The `label` argument must be unique across all `@defer`
2377+
and `@stream` directives in the document. Variables are disallowed (via
23772378
[Defer And Stream Directive Labels Are Unique](#sec-Defer-And-Stream-Directive-Labels-Are-Unique))
23782379
because their values may not be known during validation.
23792380

@@ -2389,9 +2390,9 @@ directive @stream(
23892390

23902391
The `@stream` directive may be provided for a field whose type incorporates a
23912392
`List` type modifier. The directive enables returning a partial list initially,
2392-
followed by additional items in one or more _execution update result_. If the
2393-
field type incorporates multiple `List` type modifiers, only the outermost list
2394-
is streamed.
2393+
followed by additional items in one or more _incremental stream update result_.
2394+
If the field type incorporates multiple `List` type modifiers, only the
2395+
outermost list is streamed.
23952396

23962397
Note: The mechanism through which items are streamed is implementation-defined
23972398
and may use technologies such as asynchronous iterators.
@@ -2419,9 +2420,9 @@ query myQuery($shouldStream: Boolean! = true) {
24192420
- `label: String` - An optional string literal used by GraphQL clients to
24202421
identify data in the _incremental stream_ and associate it with the
24212422
corresponding stream directive. If provided, the GraphQL service must include
2422-
this label in the corresponding _pending result_ within the _incremental
2423-
stream_. The `label` argument must be unique across all `@defer` and `@stream`
2424-
directives in the document. Variables are disallowed (via
2423+
this label in the corresponding _incremental pending notice_ within the
2424+
_incremental stream_. The `label` argument must be unique across all `@defer`
2425+
and `@stream` directives in the document. Variables are disallowed (via
24252426
[Defer And Stream Directive Labels Are Unique](#sec-Defer-And-Stream-Directive-Labels-Are-Unique))
24262427
because their values may not be known during validation.
24272428
- `initialCount: Int! = 0` - The number of list items to include initially when

0 commit comments

Comments
 (0)