At a glance
- Identifier: #1023
- Stage: RFC X / Superseded
- Champion: @yaacovCR
- Latest activity: Marked as Superseded on 2025-06-12
- Spec PR: https://github.com/graphql/graphql-spec/pull/1023
- Related:
Spec PR description
These spec edits should correspond to the working implementation at https://github.com/graphql/graphql-js/pull/3862 demonstrating incremental delivery without branching.
[The diff to main might be helpful, but this is built on top of the amazing https://github.com/graphql/graphql-spec/pull/742 and so the diff from that branch could be more useful.]
Other efforts are in the works to avoid branching, specifically #1018 and #1020. As far as I can tell, the main distinction of the approach taken by this PR is that the implementation and spec changes show how one can start executing deferred fragments semi-immediately (i.e. after deferring in an implementation-specific way), rather than waiting for the entire initial result to be emitted. This is not required -- one could still be compliant with the spec by deferring all the way until the initial result completes! In fact, how one defers is not per se observable and so the spec cannot mandate much about it with great normative force. But -- and I think this is important -- this PR and the implementation PR provide an algorithm/implementation/spec changes that give servers the flexibility to do what they think is right in that regard, and that might be desirable.
As of this moment, I am fairly confident in the implementation PR over at graphql-js, and the spec PR should generally correspond, demonstrating:
- the
Field Group,Defer Usage, andStream Usagerecord types that contain the complex information derived from the operation during field collection - the new
Publisherconstruct, which keeps track of when to release payloads - the change from a single parent
Async Payloadrecord to potentially multiple parents - the deferMap, which maps
Defer Usagerecords to individualDeferred FragmentAsync Payloadrecords - the tracking mechanism by which
Deferred Fragmentrecords are notified as complete, namely theAddPendingDeferredFieldandReportDeferredValuealgorithms
Deduplication and Payload Format
The implementation and these spec edits do not currently included deduplication, and the payload format is the same as the deferred fragment, but that can be easily changed as per below.
TLDR: it can be easily changed.
Adding deduplication
The algorithm includes events and handlers corresponding to:
- the unique completion of each fields
- the successful completion of each overall deferred fragment at a given path.
On events of type 1, fields are forwarded to each deferred fragment, and on events of type 2, the fragment including potentially duplicate values is published. We can easily change the algorithm to do something different in response to each event, without the use of WeakMap or any sort of long-lived cache.
For example:
- On events of type 1, we could immediately publish the field values
- On events of type 2, we could publish a completion notice for the fragment
Or, potentially:
- On events of type 1, we could add the field to a list of values ready to be sent -- once the first payload completes.
- On events of type 2, we could send a completion notice for the fragment and release any pending field values associated with the fragment.
Or, we could do something more complicated:
We could modify the field group collection algorithm to defined "building blocks" or subset of the deferred fragments that will combine in a predictable way no matter the order of deferred fragment completion.
- On events of type 1A, field completion, we could notify the "building block" that a field is ready.
- On events of type 1B, "building block" completion, we could notify the deferred fragment that a building block is ready
- On events of type 2, all "building blocks" for a field are ready, we could send a completion notice for the fragment and release any pending "building blocks" associated with the fragment.
** huge thanks to @urigo and @dotansimha of the guild for sponsoring my open-source work on this. **
Timeline
- Marked as Superseded on 2025-06-12 by benjie
- Advanced to RFC 0 on 2023-11-27 by benjie
- Spec PR closed on 2023-05-21
- Top comment edited on 2023-03-27 by yaacovCR
- Top comment edited on 2023-03-24 by yaacovCR
- Spec PR created on 2023-03-24 by yaacovCR
- @yaacovCR committed "CollectFields does not require path or asyncRecord (#11)" on 2023-01-16
- @robrichard committed "replace server with service" on 2023-01-15
- @robrichard committed "fix typo" on 2022-12-05
- @yaacovCR committed "fix parentRecord argument in ExecuteStreamField (#7)" on 2022-11-29
- @yaacovCR committed "spec edits for incremental delivery without branching" on 2022-11-27
- @robrichard committed "clarify label is not required" on 2022-11-23
- 2 commits pushed on 2022-11-22
- @robrichard committed "Raise a field error if defer/stream encountered during subscription e…"
- @robrichard committed "Add validation rule for defer/stream on subscriptions"
- @yaacovCR committed "Add error handling for stream iterators (#5)" on 2022-11-21
- @yaacovCR committed "fix typos (#6)" on 2022-11-18
- @yaacovCR committed "remove ResolveFIeldGenerator (#4)" on 2022-11-16
- @yaacovCR committed "small fixes (#3)" on 2022-11-07
- @robrichard committed "update on hasNext" on 2022-11-01
- @robrichard committed "link to note on should" on 2022-10-12
- 3 commits pushed on 2022-09-09
- @robrichard committed "typo"
- @robrichard committed "improve non-null example"
- @robrichard committed "Add FilterSubsequentPayloads algorithm"
- 11 commits pushed on 2022-09-08
- @robrichard committed "Add error boundary behavior"
- @robrichard committed "defer/stream response => payload"
- @robrichard committed "event stream => response stream"
- @robrichard committed "link to path section"
- @robrichard committed "use case no dash"
- @robrichard committed "remove "or null""
- @robrichard committed "add detailed incremental example"
- @robrichard committed "update label validation rule"
- @robrichard committed "clarify hasNext on incremental example"
- @robrichard committed "clarify canceling of subsequent payloads"
- @robrichard committed "Add examples for non-null cases"
- @robrichard committed "clarify null behavior of if" on 2022-08-24
- 2 commits pushed on 2022-08-23
- @robrichard committed "if: Boolean! = true"
- @robrichard committed "address pr feedback"
- 7 commits pushed on 2022-08-18
- @robrichard committed "Introduce @defer and @stream."
- @robrichard committed "Raise a field error if initialCount is less than zero"
- @robrichard committed "wait for parent async record to ensure correct order of payloads"
- @robrichard committed "spec updates to reflect latest discussions"
- @robrichard committed "Note about mutation execution order"
- @robrichard committed "minor change for uniqueness"
- @robrichard committed "fix typos"
- @robrichard committed "Align deferred fragment field collection with reference implementation" on 2022-08-03
- 3 commits pushed on 2022-06-09
- @robrichard committed "add missing line"
- @robrichard committed "fix ExecuteRequest"
- @robrichard committed "fix response"
- 2 commits pushed on 2022-03-23
- @robrichard committed "fix wrong quotes"
- @robrichard committed "remove label/path requirement"
- @robrichard committed "Clarification on labels" on 2022-03-08
- @robrichard committed "add validation “Defer And Stream Directive Labels Are Unique”" on 2022-03-07
- 2 commits pushed on 2022-02-07
- @robrichard committed "stream if argument, indexPath -> itemPath"
- @robrichard committed "Clarify stream only applies to outermost list of multi-dimensional ar…"
- @robrichard committed "deferDirective and visitedFragments" on 2022-02-02
- @robrichard committed "fix typo" on 2022-01-21
- @robrichard committed "add isCompletedIterator to AsyncPayloadRecord to track completed iter…" on 2021-12-30
- 2 commits pushed on 2021-12-20
- @robrichard committed "Simplify execution, payloads should begin execution immediately"
- @robrichard committed "Clarify error handling"
- 2 commits pushed on 2021-12-06
- @robrichard committed "data is not necessarily an object in subsequent payloads"
- @robrichard committed "add Defer And Stream Directives Are Used On Valid Root Field rule"
- @robrichard committed "fix typo" on 2021-11-26
- @robrichard committed "allow extensions only subsequent payloads" on 2021-11-25
- @robrichard committed "clarify negative values of initialCount" on 2021-11-20
- @robrichard committed "clarification on defer/stream requirement" on 2021-11-19
- @robrichard committed "Update Section 3 -- Type System.md" on 2021-05-15
- 3 commits pushed on 2021-02-17
- @robrichard committed "fix typos"
- @robrichard committed "clear up that it is legal to support either defer or stream individually"
- @robrichard committed "Add sumary of arguments to Type System"