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
@deferand@streamselections; - a {"pending"} entry containing two incremental pending notices, one for the
@deferselection and for the the@streamselection, indicating that these results will be delivered in a later incremental stream update result; - a {"hasNext"} entry with the value {true}, indicating that the response is not yet complete.
If an error were to occur, it would also have an {"errors"} entry; but not in this example.
{
"data": {
"person": {
"name": "Luke Skywalker",
"films": [{ "title": "A New Hope" }]
}
},
"pending": [
{ "id": "0", "path": ["person"], "label": "homeWorldDefer" },
{ "id": "1", "path": ["person", "films"], "label": "filmsStream" }
],
"hasNext": true
}Depending on the behavior of the backend and the time at which the deferred and streamed resources resolve, the stream may produce results in different orders. In this example, our first incremental stream update result contains the deferred data and the first streamed list item. There is one incremental completion notice, indicating that the deferred data has been completely delivered.
{
"incremental": [
{
"id": "0",
"data": { "homeWorld": { "name": "Tatooine" } }
},
{
"id": "1",
"items": [{ "title": "The Empire Strikes Back" }]
}
],
"completed": [
{"id": "0"}
]
"hasNext": true
}The second incremental stream update result contains the final stream results. In this example, the underlying iterator does not close synchronously so {"hasNext"} is set to {true}. If this iterator did close synchronously, {"hasNext"} could be set to {false} and make this the final incremental stream update result.
{
"incremental": [
{
"id": "1",
"items": [{ "title": "Return of the Jedi" }]
}
],
"hasNext": true
}When the underlying iterator of the films field closes there is no more data
to deliver, so the third and final incremental stream update result sets
{"hasNext"} to {false} to indicate the end of the incremental stream.
{
"hasNext": false
}query {
person(id: "cGVvcGxlOjE=") {
...HomeWorldFragment @defer(label: "homeWorldDefer")
...NameAndHomeWorldFragment @defer(label: "nameAndWorld")
firstName
}
}
fragment HomeWorldFragment on Person {
homeWorld {
name
terrain
}
}
fragment NameAndHomeWorldFragment on Person {
firstName
lastName
homeWorld {
name
}
}In this example the response is an incremental stream of the following results.
The initial incremental stream result contains the results of the firstName
field. Even though it is also present in the HomeWorldFragment, it must be
returned in the initial incremental stream result because it is also defined
outside of any fragments with the @defer directive. Additionally, there are
two incremental pending notices indicating that results for both @defers in
the query will be delivered in later incremental stream update result.
{
"data": {
"person": {
"firstName": "Luke"
}
},
"pending": [
{ "id": "0", "path": ["person"], "label": "homeWorldDefer" },
{ "id": "1", "path": ["person"], "label": "nameAndWorld" }
],
"hasNext": true
}In this example, the first incremental stream update result contains the
deferred data from HomeWorldFragment. There is one incremental completion
notice, indicating that HomeWorldFragment has been completely delivered.
Because the homeWorld field is present in two separate @defers, it is
separated into its own incremental result. In this example, this incremental
result contains the id "0", but since the name field was included in both
HomeWorldFragment and NameAndHomeWorldFragment, an id of "1" would also be
a valid response.
The second incremental result in this incremental stream update result
contains the data for the terrain field. This incremental result contains a
{"subPath"} entry to indicate to clients that the response position of this
result can be determined by concatenating: the path from the incremental
pending notice for id "0", and the value of this {"subPath"} entry.
{
"incremental": [
{
"id": "0",
"data": { "homeWorld": { "name": "Tatooine" } }
},
{
"id": "0",
"subPath": ["homeWorld"],
"data": { "terrain": "desert" }
}
],
"completed": [{ "id": "0" }],
"hasNext": true
}The second incremental stream update result contains the remaining data from
the NameAndHomeWorldFragment. lastName is the only remaining field from this
selection that has not been delivered in a previous result. With this field now
delivered, clients are informed that the NameAndHomeWorldFragment has been
completed by the presence of the associated incremental completion notice.
Additionally, {"hasNext"} is set to {false} indicating the end of the
incremental stream.
This example demonstrates that it is necessary for clients to process the entire incremental stream, as both the initial data and previous incremental results (with a potentially different value for {"id"}) may be required to complete a deferred fragment.
{
"incremental": [
{
"id": "1",
"data": { "lastName": "Skywalker" }
}
],
"completed": [{ "id": "1" }],
"hasNext": false
}