Field Selection Merging
Formal Specification
- Let set be any selection set defined in the GraphQL document.
- FieldsInSetCanMerge ( set ) must be true.
(
set
)
- Let fieldsForName be the set of selections with a given response name in set including visiting fragments and inline fragments.
- Given each pair of members
fieldA
and
fieldB
in
fieldsForName
:
- SameResponseShape ( fieldA , fieldB ) must be true.
- If the parent types of
fieldA
and
fieldB
are equal or if either is not an Object Type:
- fieldA and fieldB must have identical field names.
- fieldA and fieldB must have identical sets of arguments.
- Let mergedSet be the result of adding the selection set of fieldA and the selection set of fieldB .
- FieldsInSetCanMerge ( mergedSet ) must be true.
(
fieldA
,
fieldB
)
- Let typeA be the return type of fieldA .
- Let typeB be the return type of fieldB .
- If
typeA
or
typeB
is Non‐Null.
- typeA and typeB must both be Non‐Null.
- Let typeA be the nullable type of typeA
- Let typeB be the nullable type of typeB
- If
typeA
or
typeB
is List.
- typeA and typeB must both be List.
- Let typeA be the item type of typeA
- Let typeB be the item type of typeB
- Repeat from step 3.
- If
typeA
or
typeB
is Scalar or Enum.
- typeA and typeB must be the same type.
- Assert: typeA and typeB are both composite types.
- Let mergedSet be the result of adding the selection set of fieldA and the selection set of fieldB .
- Let fieldsForName be the set of selections with a given response name in mergedSet including visiting fragments and inline fragments.
- Given each pair of members
subfieldA
and
subfieldB
in
fieldsForName
:
- SameResponseShape ( subfieldA , subfieldB ) must be true.
Explanatory Text
If multiple field selections with the same response names are encountered during execution, the field and arguments to execute and the resulting value should be unambiguous. Therefore any two field selections which might both be encountered for the same object are only valid if they are equivalent.
For simple hand‐written GraphQL, this rule is obviously a clear developer error, however nested fragments can make this difficult to detect manually.
The following selections correctly merge:
fragment mergeIdenticalFields on Dog {
name
name
}
fragment mergeIdenticalAliasesAndFields on Dog {
otherName: name
otherName: name
}
The following is not able to merge:
fragment conflictingBecauseAlias on Dog {
name: nickname
name
}
Identical arguments are also merged if they have identical arguments. Both values and variables can be correctly merged.
For example the following correctly merge:
fragment mergeIdenticalFieldsWithIdenticalArgs on Dog {
doesKnowCommand(dogCommand: SIT)
doesKnowCommand(dogCommand: SIT)
}
fragment mergeIdenticalFieldsWithIdenticalValues on Dog {
doesKnowCommand(dogCommand: $dogCommand)
doesKnowCommand(dogCommand: $dogCommand)
}
The following do not correctly merge:
fragment conflictingArgsOnValues on Dog {
doesKnowCommand(dogCommand: SIT)
doesKnowCommand(dogCommand: HEEL)
}
fragment conflictingArgsValueAndVar on Dog {
doesKnowCommand(dogCommand: SIT)
doesKnowCommand(dogCommand: $dogCommand)
}
fragment conflictingArgsWithVars on Dog {
doesKnowCommand(dogCommand: $varOne)
doesKnowCommand(dogCommand: $varTwo)
}
fragment differingArgs on Dog {
doesKnowCommand(dogCommand: SIT)
doesKnowCommand
}
The following fields would not merge together, however both cannot be encountered against the same object, so they are safe:
fragment safeDifferingFields on Pet {
... on Dog {
volume: barkVolume
}
... on Cat {
volume: meowVolume
}
}
fragment safeDifferingArgs on Pet {
... on Dog {
doesKnowCommand(dogCommand: SIT)
}
... on Cat {
doesKnowCommand(catCommand: JUMP)
}
}
However, the field responses must be shapes which can be merged. For example, scalar values must not differ. In this example,someValue
might be aString
or anInt
:
fragment conflictingDifferingResponses on Pet {
... on Dog {
someValue: nickname
}
... on Cat {
someValue: meowVolume
}
}