Skip to content

Commit 6a639da

Browse files
committed
[validation] Loosen overlapping field validation rules
This loosens the rules for fields of the same response name which occur in the same selection set. Implemented in JS by graphql/graphql-js#230 and graphql/graphql-js#229 This allows for fields of the same response name to differ in a few conditions deemed to be safe where previously the validation was too conservative. The currently supported directives: @Skip and @include do not create an ambiguous or erroneous query when used on conflicting fields in a divergent fashion. In fact, this may be intended when many different conditions should result in the same outcome. For example: ```graphql { field @include(if: $firstCondition) field @include(if: $secondCondition) } ``` This example could be considered as the intent of fetching `field` given `$firstCondition || $secondCondition`. While this example is contrived, there are more complex examples where such fields are nested within fragments that this condition is reasonable. The following example is now legal, since no object can be both the concrete object type "Cat" and "Dog" at the same time, so the response shape is still unambiguous provided the types are known. ```graphql { ... on Cat { name } ... on Dog { name: nickname } } ``` However the following is still not legal, since it results in ambiguity: ```graphql { name ... on Dog { name: nickname } } ```
1 parent 5d5bcd7 commit 6a639da

File tree

1 file changed

+35
-25
lines changed

1 file changed

+35
-25
lines changed

spec/Section 5 -- Validation.md

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -234,21 +234,24 @@ fragment directFieldSelectionOnUnion on CatOrDog {
234234
** Formal Specification **
235235

236236
* Let {set} be any selection set defined in the GraphQL document
237-
* Let {setForKey} be the set of selections with a given response key in {set}
238-
* All members of {setForKey} must:
239-
* Have identical target fields
240-
* Have identical sets of arguments.
241-
* Have identical sets of directives.
237+
* Let {fieldsForName} be the set of selections with a given response name in
238+
{set} including visiting fragments and inline fragments.
239+
* Given each pair of members {fieldA} and {fieldB} in {fieldsForName}:
240+
* If the parent types of {fieldA} and {fieldB} are equal or if either is not
241+
an Object Type:
242+
* {fieldA} and {fieldB} must have identical field names.
243+
* {fieldA} and {fieldB} must have identical return type.
244+
* {fieldA} and {fieldB} must have identical sets of arguments.
242245

243246
** Explanatory Text **
244247

245-
Selection names are de-duplicated and merged for validation, but the target
246-
field, arguments, and directives must all be identical.
248+
If multiple fields selections with the same response names are encountered
249+
during execution, the result should be unambiguous. Therefore any two field
250+
selections which might both be encountered for the same object are only valid if
251+
they are equivalent.
247252

248-
For human-curated GraphQL, this rules seem a bit counterintuitive since it
249-
appears to be clear developer error. However in the presence of nested
250-
fragments or machine-generated GraphQL, requiring unique selections is a
251-
burdensome limitation on tool authors.
253+
For simple hand-written GraphQL, this rule is obviously a clear developer error,
254+
however nested fragments can make this difficult to detect manually.
252255

253256
The following selections correctly merge:
254257

@@ -307,26 +310,33 @@ fragment conflictingArgsWithVars on Dog {
307310
doesKnowCommand(dogCommand: $varOne)
308311
doesKnowCommand(dogCommand: $varTwo)
309312
}
310-
```
311313
312-
The same logic applies to directives. The set of directives on each selection
313-
with the same response key in a given scope must be identical.
314+
fragment differingArgs on Dog {
315+
doesKnowCommand(dogCommand: SIT)
316+
doesKnowCommand
317+
}
318+
```
314319

315-
The following is valid:
320+
The following would not merge together, however both cannot be encountered
321+
against the same object:
316322

317323
```graphql
318-
fragment mergeSameFieldsWithSameDirectives on Dog {
319-
name @include(if: true)
320-
name @include(if: true)
324+
fragment safeDifferingFields on Pet {
325+
... on Dog {
326+
name: nickname
327+
}
328+
... on Cat {
329+
name
330+
}
321331
}
322-
```
323-
324-
and the following is invalid:
325332

326-
```!graphql
327-
fragment conflictingDirectiveArgs on Dog {
328-
name @include(if: true)
329-
name @include(if: false)
333+
fragment safeDifferingArgs on Pet {
334+
... on Dog {
335+
doesKnowCommand(dogCommand: SIT)
336+
}
337+
... on Cat {
338+
doesKnowCommand(catCommand: JUMP)
339+
}
330340
}
331341
```
332342

0 commit comments

Comments
 (0)