Skip to content
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
210 changes: 210 additions & 0 deletions spec/Appendix E -- Examples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
# E. Appendix: Examples

## Incremental Delivery Examples

### Example 1 - A query containing both defer and stream

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

The response to this request will be an _incremental stream_ consisting of an
_initial incremental stream result_ followed by one or more _incremental stream
update result_.

The _initial incremental stream result_ has:

- a {"data"} entry containing the results of the GraphQL operation except for
the `@defer` and `@stream` selections;
- a {"pending"} entry containing two _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 {"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
{
"data": {
"person": {
"name": "Luke Skywalker",
"films": [{ "title": "A New Hope" }]
}
},
"pending": [
{ "id": "0", "path": ["person"], "label": "homeWorldDefer" },
{ "id": "1", "path": ["person", "films"], "label": "filmsStream" }
],
"hasNext": true
}
```

Depending on the behavior of the backend and the time at which the deferred and
streamed resources resolve, the stream may produce results in different orders.
In this example, our first _incremental stream update result_ contains the
deferred data and the first streamed list item. There is one _completed result_,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe change "completed result" to "incremental completion result" to tie it in with the "incremental" naming? (Also to be more specific.)

Suggested change
deferred data and the first streamed list item. There is one _completed result_,
deferred data and the first streamed list item. There is one _incremental completion result_,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated now to incremental completion notice

indicating that the deferred data has been completely delivered.

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

The second _incremental stream update result_ contains the final stream results.
In this example, the underlying iterator does not close synchronously so
{"hasNext"} is set to {true}. If this iterator did close synchronously,
{"hasNext"} would be set to {false} and this would be the final incremental
stream update result.

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

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
{
"hasNext": false
}
```

### Example 2 - A query containing overlapping defers

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

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

In this example the response is an _incremental stream_ of the following
results.

The _initial incremental stream result_ contains the results of the `firstName`
field. Even though it is also present in the `HomeWorldFragment`, it must be
returned in the initial incremental stream result because it is also defined
outside of any fragments with the `@defer` directive. Additionally, there are
two _pending result_ indicating that results for both `@defer`s in the query
will be delivered in later _incremental stream update result_.

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

In this example, the first _incremental stream update result_ contains the
deferred data from `HomeWorldFragment`. There is one _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 _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
{
"incremental": [
{
"id": "0",
"data": { "homeWorld": { "name": "Tatooine" } }
},
Comment on lines +180 to +183
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel like the weirdness of this needs more calling out - name belongs to both id:0 and id:1, so we're kind of using id:0 as a shortcut (but id:1 would also work). We should make it clear to a client that it can't form HomeWorldFragment from solely the root data plus the id:0 stuff and NameAndHomeWorldFragment from solely the root data and the id:1 stuff - they must merge the trees before extracting the data.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{
"id": "0",
"subPath": ["homeWorld"],
"data": { "terrain": "desert" }
}
],
"completed": [{ "id": "0" }],
"hasNext": true
}
```

The second _incremental stream update result_ contains the remaining data from
the `NameAndHomeWorldFragment`. `lastName` is the only remaining field from this
selection that has not been delivered in a previous result. With this field now
delivered, clients are informed that the `NameAndHomeWorldFragment` has been
completed by the presence of the associated _completed result_. Additionally,
{"hasNext"} is set to {false} indicating the end of the _incremental stream_.

```json example
{
"incremental": [
{
"id": "1",
"data": { "lastName": "Skywalker" }
}
],
"completed": [{ "id": "1" }],
"hasNext": false
}
```
2 changes: 2 additions & 0 deletions spec/GraphQL.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
11 changes: 6 additions & 5 deletions spec/Section 3 -- Type System.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.

Expand Down Expand Up @@ -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.
Expand Down
Loading
Loading