Skip to content

Fix typos in the GraphQL Defer and Stream Directives RFC #807

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 18, 2021
Merged
Changes from all 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
38 changes: 19 additions & 19 deletions rfcs/DeferStream.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ Given a query where some fields are expensive and non-essential, non-essential f
## Prefetching
This technique involves optimistically fetching data based on a prediction that a user will execute an action. Prefetching can be one of the most effective ways of reducing latency. However, a significant tradeoff with prefetching that a lot of applications cannot afford is **increased server cost due to incorrect predictions.** With an unsophisticated prefetch algorithm, applications can easily overfetch by a factor of 10 fold.

# Proposal: Incrementally deliver data with @defer and @stream
# Proposal: Incrementally deliver data with `@defer` and `@stream`

This proposal would introduce @stream and @defer directives which clients could use to communicate the relative priority of requested data to GraphQL implementations. Furthermore this proposal would enable GraphQL APIs to split requested data across multiple response payloads in order of priority. The goal of this proposal is to enable applications to reduce latency without increasing server cost or resource contention.
This proposal would introduce `@stream` and `@defer` directives which clients could use to communicate the relative priority of requested data to GraphQL implementations. Furthermore this proposal would enable GraphQL APIs to split requested data across multiple response payloads in order of priority. The goal of this proposal is to enable applications to reduce latency without increasing server cost or resource contention.

While both incremental delivery and GraphQL subscriptions send multiple payloads over time, **incremental delivery is _not_ intended to enable applications to respond to real-time changes.** Consequently streams opened for incremental delivery are expected to be short-lived. **Implementations are not required to reflect interleaving mutations which occur during incremental delivery.** Assuming there are no interleaving mutations, combining together the various payloads in an incrementally delivered response should produce the same output as if that response was not delivered incrementally.

Expand All @@ -36,7 +36,7 @@ Facebook has been using Incremental Delivery at scale since 2017, including on m
GraphQL servers will not be required to implement `@defer` and/or `@stream`. If they are implemented, they will be required to follow the proposed specification. Servers that do not implement `@defer` and/or `@stream` should not expose these directives in their schema. Queries containing these directives that are sent to an unsupported server should fail validation.

## `@defer`
The @defer directive may be specified on a fragment spread to imply de-prioritization, that causes the fragment to be omitted in the initial response, and delivered as a subsequent response afterward. A query with @defer directive will cause the request to potentially return multiple responses, where non-deferred data is delivered in the initial response and data deferred delivered in a subsequent response. `@include` and `@skip` take presedence over `@defer`.
The `@defer` directive may be specified on a fragment spread to imply de-prioritization, that causes the fragment to be omitted in the initial response, and delivered as a subsequent response afterward. A query with `@defer` directive will cause the request to potentially return multiple responses, where non-deferred data is delivered in the initial response and data deferred delivered in a subsequent response. `@include` and `@skip` take precedence over `@defer`.

### `@defer` arguments
* `if: Boolean`
Expand All @@ -48,9 +48,9 @@ The @defer directive may be specified on a fragment spread to imply de-prioritiz

## `@stream`

The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take presedence over `@stream`.
The `@stream` directive may be provided for a field of `List` type so that the backend can leverage technology such asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. `@include` and `@skip` take precedence over `@stream`.

### `@stream `arguments
### `@stream` arguments
* `if: Boolean`
* When `true` field may be streamed, if omitted defaults to `true`.
* `label: String`
Expand All @@ -64,23 +64,23 @@ The `@stream` directive may be provided for a field of `List` type so that the b

When an operation contains `@defer` or `@stream` directives, the GraphQL execution will return multiple payloads. The first payload is the same shape as a standard GraphQL response. Any fields that were only requested on a fragment that is deferred will not be present in this payload. Any list fields that are streamed will only contain the initial list items.

Each subsequent payload will be an object with the following properties
* `label`: The string that was passed to the label argument of the `@defer` or `@stream` directive that corresponds to this results.
Each subsequent payload will be an object with the following properties:
* `label`: The string that was passed to the label argument of the `@defer` or `@stream` directive that corresponds to this result.
* `data`: The data that is being delivered incrementally.
* `path`: a list of keys (with plural indexes) from the root of the response to the insertion point that informs the client how to patch a subsequent delta payload into the original payload.
* `hasNext`: A boolean that is present and `true` when there are more payloads that will be sent for this operation. The last payload in a multi payload response should return `hasNext: false`. `hasNext` is not required for single-payload responses to preserve backwards compatibility.
* `hasNext`: A boolean that is present and `true` when there are more payloads that will be sent for this operation. The last payload in a multi-payload response should return `hasNext: false`. `hasNext` is not required for single-payload responses to preserve backwards compatibility.
* `errors`: An array that will be present and contain any field errors that are produced while executing the deferred or streamed selection set.
* `extensions`: For implementors to extend the protocol
* `extensions`: For implementors to extend the protocol.

Note: The `label` field is not a unique identifier for payloads. There may be multiple payloads with the same label for either payloads for `@stream`, or payloads from a `@defer` fragment under a list field. The combination of `label` and `path` will be unique among all payloads.

## Server requirements for `@defer` and `@stream`

The ability to defer/stream parts of a response can have a potentially significant impact on application performance. Developers generally need clear, predictable control over their application's performance. It is highly recommended that the GraphQL server honor the @defer and @stream directives on each execution. However, the specification will allow advanced use-cases where the server can determine that it is more performant to not defer/stream. Therefore, GraphQL clients should be able to process a response that ignores the defer/stream directives.
The ability to defer/stream parts of a response can have a potentially significant impact on application performance. Developers generally need clear, predictable control over their application's performance. It is highly recommended that the GraphQL server honor the `@defer` and `@stream` directives on each execution. However, the specification will allow advanced use-cases where the server can determine that it is more performant to not defer/stream. Therefore, GraphQL clients should be able to process a response that ignores the defer/stream directives.

This also applies to the `initialCount` argument on the `@stream` directive. Clients should be able to process a streamed response that contains a different number of initial list items than what was specified in the `initialCount` argument.

## Example Query with @defer and @stream
## Example Query with `@defer` and `@stream`

```
{
Expand Down Expand Up @@ -133,30 +133,30 @@ fragment GroupAdminFragment {
```

## Benefits of incremental delivery
* Make GraphQL a great choice for applications which demand responsiveness
* Make GraphQL a great choice for applications which demand responsiveness.
* Enable interoperability between different GraphQL clients and servers without restricting implementation.
* Enable a strong tooling ecosystem (including GraphiQL).
* Provide concrete guidance to implementers
* Provide guidance to developers evaluating whether to adopt incremental delivery
* Provide concrete guidance to implementers.
* Provide guidance to developers evaluating whether to adopt incremental delivery.

## Use case guidance:
The goal of incremental delivery is to prioritize the delivery of essential data. Even though incremental delivery delivers data over time, the response describes the data at a particular point in time. Therefore, it is not necessary to reflect real time changes to the data model in incremental delivery. Implementers of @defer and @stream are not obligated to address interleaving mutations during the execution of @defer and @stream.
The goal of incremental delivery is to prioritize the delivery of essential data. Even though incremental delivery delivers data over time, the response describes the data at a particular point in time. Therefore, it is not necessary to reflect real time changes to the data model in incremental delivery. Implementers of `@defer` and `@stream` are not obligated to address interleaving mutations during the execution of `@defer` and `@stream`.

GraphQL Subscription is an event-oriented approach to capture real time data changes. It intends to describe interesting events that happen over a period of time and delivers updated value that “invalidate” previous values.
GraphQL Subscription is an event-oriented approach to capture real time data changes. It intends to describe interesting events that happen over a period of time and delivers updated values that “invalidate” previous values.

## Implementation details of @stream and @defer
## Implementation details of `@stream` and `@defer`

For GraphQL communications built on top of HTTP, a natural and compatible technology to leverage is HTTP chunked encoding to implement a stream of responses for incremental delivery.

## Caveats

### Type Generation
Supporting @defer can add complexity to type-generating clients. Separate types will need to be generated for the different deferred fragments. These clients will need to use the `label` field to determine which fragments have been fulfilled to ensure the application is using the correct types.
Supporting `@defer` can add complexity to type-generating clients. Separate types will need to be generated for the different deferred fragments. These clients will need to use the `label` field to determine which fragments have been fulfilled to ensure the application is using the correct types.

### Object Consistency
The GraphQL spec does not currently support object identification or consistency. It is currently possible for the same object to be returned in multiple places in a query. If that object changes while the resolvers are running, the query could return inconsistent results. `@defer`/`@stream` does not increase the likelihood of this, as the server still attempts to resolve everything as fast as it can. The only difference is some results can be returned to the client sooner. This proposal does not attempt to address this issue.

### Can @defer/@stream increase risk of a denial of service attack?
### Can `@defer`/`@stream` increase risk of a denial of service attack?
This is currently a risk in GraphQL servers that do not implement any kind of query limiting as arbitrarily complex queries can be sent. Adding `@defer` may add some overhead as the server will now send parts of the query earlier than it would have without `@defer`, but it does not allow for any additional resolving that was not previously possible.


Expand Down