Skip to content
Open
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
120 changes: 97 additions & 23 deletions spec/Section 6 -- Execution.md
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ name_ and its associated _field set_. A _collected fields map_ may be produced
from a selection set via {CollectFields()} or from the selection sets of all
entries of a _field set_ via {CollectSubfields()}.

:: A _field set_ is an ordered set of selected fields that share the same
:: A _field set_ is an ordered set of _field details_ that share the same
_response name_ (the field alias if defined, otherwise the field's name).
Validation ensures each field in the set has the same name and arguments,
however each may have different subfields (see:
Expand Down Expand Up @@ -439,10 +439,46 @@ The depth-first-search order of each _field set_ produced by {CollectFields()}
is maintained through execution, ensuring that fields appear in the executed
response in a stable and predictable order.

CollectFields(objectType, selectionSet, variableValues, visitedFragments):
CollectFields(objectType, selectionSet, variableValues, deferUsage,
visitedFragments):
Comment on lines +442 to +443

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.

move to directly above the algorithm definition


The {CollectFields()} algorithm makes use of the following data types:

:: A _defer usage_ is an unordered map representing the usage of a `@defer`
directive within a given operation. _Defer usage_ are "abstract" in that they
include information about the `@defer` directive from the AST of the GraphQL
document. A single _defer usage_ may be used to create many "concrete" Delivery
Groups when a `@defer` is included within a list type.

A _defer usage_ contains the following information:
Comment on lines +447 to +453

@benjie benjie Jul 16, 2026

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.

Suggested change
:: A _defer usage_ is an unordered map representing the usage of a `@defer`
directive within a given operation. _Defer usage_ are "abstract" in that they
include information about the `@defer` directive from the AST of the GraphQL
document. A single _defer usage_ may be used to create many "concrete" Delivery
Groups when a `@defer` is included within a list type.
A _defer usage_ contains the following information:
:: A _defer usage_ represents the usage of a `@defer` directive during field
collection; it contains the following information:

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.

Update algorithm references to defer usage to require a map datastructure

- Let {fragmentDeferUsage} be an unordered map containing {label} and
        {parentDeferUsage}.
+ Let {fragmentDeferUsage} be {label} and {parentDeferUsage}.


- {label}: the `label` argument provided by the given `@defer` directive, if
any, otherwise {undefined}.
- {parentDeferUsage}: a defer usage corresponding to the `@defer` directive
enclosing this `@defer` directive, if any, otherwise {undefined}.

The {parentDeferUsage} entry is used to build distinct Execution Groups as
discussed within the Execution Plan Generation section below.

:: A _field detail_ is an unordered map containing the following entries:

- {field}: the field selection.
- {deferUsage}: the _defer usage_ enclosing the selection, if any, otherwise
{undefined}.
Comment on lines +466 to +467

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.

Suggested change
- {deferUsage}: the _defer usage_ enclosing the selection, if any, otherwise
{undefined}.
- {deferUsage}: the _defer usage_ enclosing the selection, if any.


:: A _collected fields map_ is an ordered map of _response name_ to lists of
Field Details.

The {CollectFields()} algorithm returns:

- {collectedFieldsMap}: the _collected fields map_ for the fields in the
selection set.
- {newDeferUsages}: a list of new _defer usage_ encountered during this field
collection.
Comment on lines +472 to +477

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.

Suggested change
The {CollectFields()} algorithm returns:
- {collectedFieldsMap}: the _collected fields map_ for the fields in the
selection set.
- {newDeferUsages}: a list of new _defer usage_ encountered during this field
collection.


- If {visitedFragments} is not provided, initialize it to the empty set.
- Initialize {collectedFieldsMap} to an empty ordered map of ordered sets.
- Initialize {newDeferUsages} to an empty list.
- For each {selection} in {selectionSet}:
- If {selection} provides the directive `@skip`, let {skipDirective} be that
directive.
Expand All @@ -457,15 +493,26 @@ CollectFields(objectType, selectionSet, variableValues, visitedFragments):
- If {selection} is a {Field}:
- Let {responseName} be the _response name_ of {selection} (the alias if
defined, otherwise the field name).
- Let {fieldDetails} be a new unordered map containing {field} and
{deferUsage}.
- Set the corresponding entries on {fieldDetails} to {selection} and
{deferUsage}, respectively.
- Let {fieldsForResponseName} be the _field set_ value in
{collectedFieldsMap} for the key {responseName}; otherwise create the
entry with an empty ordered set.
- Add {selection} to the {fieldsForResponseName}.
- Add {fieldDetails} to the {fieldsForResponseName}.
- If {selection} is a {FragmentSpread}:
- Let {fragmentSpreadName} be the name of {selection}.
- If {selection} provides the directive `@defer` and its {if} argument is
not {false} and is not a variable in {variableValues} with the value
{false}:
- Let {deferDirective} be that directive.
- If this execution is for a subscription operation, raise an _execution
error_.

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.

Suggested change
error_.
error_.
- Otherwise let {deferDirective} be {null}.

- If {fragmentSpreadName} is in {visitedFragments}, continue with the next
{selection} in {selectionSet}.
- Add {fragmentSpreadName} to {visitedFragments}.
- If {deferDirective} is not defined:
- Add {fragmentSpreadName} to {visitedFragments}.
Comment on lines +514 to +515

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.

- Let {fragment} be the Fragment in the current Document whose name is
{fragmentSpreadName}.
- If no such {fragment} exists, continue with the next {selection} in
Expand All @@ -474,31 +521,51 @@ CollectFields(objectType, selectionSet, variableValues, visitedFragments):
- If {DoesFragmentTypeApply(objectType, fragmentType)} is {false}, continue
with the next {selection} in {selectionSet}.
- Let {fragmentSelectionSet} be the top-level selection set of {fragment}.
- Let {fragmentCollectedFieldsMap} be the result of calling
{CollectFields(objectType, fragmentSelectionSet, variableValues,
visitedFragments)}.
- For each {responseName} and {fragmentFields} in
- If {deferDirective} is defined:
- Let {label} be the corresponding entry on {deferDirective}.
- Let {parentDeferUsage} be {deferUsage}.
- Let {fragmentDeferUsage} be an unordered map containing {label} and
{parentDeferUsage}.
- Otherwise, let {fragmentDeferUsage} be {deferUsage}.
- Let {fragmentCollectedFieldsMap} and {fragmentNewDeferUsages} be the
result of calling {CollectFields(objectType, fragmentSelectionSet,
variableValues, fragmentDeferUsage, visitedFragments)}.
- For each {responseName} and {fragmentFieldSet} in
{fragmentCollectedFieldsMap}:
- Let {fieldsForResponseName} be the _field set_ value in
{collectedFieldsMap} for the key {responseName}; otherwise create the
entry with an empty ordered set.
- Add each item from {fragmentFields} to {fieldsForResponseName}.
- Add each item from {fragmentFieldSet} to {fieldsForResponseName}.
- Append all items in {fragmentNewDeferUsages} to {newDeferUsages}.
- If {selection} is an {InlineFragment}:
- Let {fragmentType} be the type condition on {selection}.
- If {fragmentType} is not {null} and {DoesFragmentTypeApply(objectType,
fragmentType)} is {false}, continue with the next {selection} in
{selectionSet}.
- Let {fragmentSelectionSet} be the top-level selection set of {selection}.
- Let {fragmentCollectedFieldsMap} be the result of calling
{CollectFields(objectType, fragmentSelectionSet, variableValues,
visitedFragments)}.
- For each {responseName} and {fragmentFields} in
- If {selection} provides the directive `@defer` and its {if} argument is
not {false} and is not a variable in {variableValues} with the value
{false}:
- Let {deferDirective} be that directive.
- If this execution is for a subscription operation, raise an _execution
error_.
- If {deferDirective} is defined:
- Let {label} be the corresponding entry on {deferDirective}.
- Let {parentDeferUsage} be {deferUsage}.
- Let {fragmentDeferUsage} be an unordered map containing {label} and
{parentDeferUsage}.
- Otherwise, let {fragmentDeferUsage} be {deferUsage}.
- Let {fragmentCollectedFieldsMap} and {fragmentNewDeferUsages} be the
result of calling {CollectFields(objectType, fragmentSelectionSet,
variableValues, fragmentDeferUsage, visitedFragments)}.
- For each {responseName} and {fragmentFieldSet} in
{fragmentCollectedFieldsMap}:
- Let {fieldsForResponseName} be the _field set_ value in
{collectedFieldsMap} for the key {responseName}; otherwise create the
entry with an empty ordered set.
- Append each item from {fragmentFields} to {fieldsForResponseName}.
- Return {collectedFieldsMap}.
- Add each item from {fragmentFieldSet} to {fieldsForResponseName}.
- Append all items in {fragmentNewDeferUsages} to {newDeferUsages}.
- Return {collectedFieldsMap} and {newDeferUsages}.

DoesFragmentTypeApply(objectType, fragmentType):

Expand All @@ -515,6 +582,10 @@ DoesFragmentTypeApply(objectType, fragmentType):
Note: The steps in {CollectFields()} evaluating the `@skip` and `@include`
directives may be applied in either order since they apply commutatively.

Note: When completing a List field, the {CollectFields} algorithm is invoked
with the same arguments for each element of the list. GraphQL Services may
choose to memoize their implementations of {CollectFields}.

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.

Make this a stronger recommendation and explain why


**Merging Selection Sets**

In order to execute the sub-selections of an object typed field, all _selection
Expand Down Expand Up @@ -550,20 +621,23 @@ resolved in the same phase with the same value.
CollectSubfields(objectType, fields, variableValues):

- Let {collectedFieldsMap} be an empty ordered map of ordered sets.
- For each {field} in {fields}:
- Let {newDeferUsages} be an empty list.
- For each {fieldDetails} in {fields}:
- Let {field} and {deferUsage} be the corresponding entries on {fieldDetails}.
- Let {fieldSelectionSet} be the selection set of {field}.
- If {fieldSelectionSet} is null or empty, continue to the next field.
- Let {fieldCollectedFieldsMap} be the result of {CollectFields(objectType,
fieldSelectionSet, variableValues)}.
- For each {responseName} and {subfields} in {fieldCollectedFieldsMap}:
- Let {subCollectedFieldsMap} and {subNewDeferUsages} be the result of
{CollectFields(objectType, fieldSelectionSet, variableValues, deferUsage)}.
- For each {responseName} and {subfields} in {subCollectedFieldsMap}:
- Let {fieldsForResponseName} be the _field set_ value in
{collectedFieldsMap} for the key {responseName}; otherwise create the
entry with an empty ordered set.
- Add each fields from {subfields} to {fieldsForResponseName}.
- Return {collectedFieldsMap}.
- Add each item from {subfields} to {fieldsForResponseName}.
- Append all items in {subNewDeferUsages} to {newDeferUsages}.
- Return {collectedFieldsMap} and {newDeferUsages}.

Note: All the {fields} passed to {CollectSubfields()} share the same _response
name_.
Note: All the {fieldDetailsList} passed to {CollectSubfields()} share the same
_response name_.

### Executing Collected Fields

Expand Down
Loading