Skip to content

Commit

Permalink
[validation] Loosen overlapping field validation rules
Browse files Browse the repository at this point in the history
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
  }
}
```
  • Loading branch information
leebyron committed Nov 13, 2015
1 parent 5d5bcd7 commit 6a639da
Showing 1 changed file with 35 additions and 25 deletions.
60 changes: 35 additions & 25 deletions spec/Section 5 -- Validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,21 +234,24 @@ fragment directFieldSelectionOnUnion on CatOrDog {
** Formal Specification **

* Let {set} be any selection set defined in the GraphQL document
* Let {setForKey} be the set of selections with a given response key in {set}
* All members of {setForKey} must:
* Have identical target fields
* Have identical sets of arguments.
* Have identical sets of directives.
* 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}:
* 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 return type.
* {fieldA} and {fieldB} must have identical sets of arguments.

** Explanatory Text **

Selection names are de-duplicated and merged for validation, but the target
field, arguments, and directives must all be identical.
If multiple fields selections with the same response names are encountered
during execution, the result 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 human-curated GraphQL, this rules seem a bit counterintuitive since it
appears to be clear developer error. However in the presence of nested
fragments or machine-generated GraphQL, requiring unique selections is a
burdensome limitation on tool authors.
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:

Expand Down Expand Up @@ -307,26 +310,33 @@ fragment conflictingArgsWithVars on Dog {
doesKnowCommand(dogCommand: $varOne)
doesKnowCommand(dogCommand: $varTwo)
}
```
The same logic applies to directives. The set of directives on each selection
with the same response key in a given scope must be identical.
fragment differingArgs on Dog {
doesKnowCommand(dogCommand: SIT)
doesKnowCommand
}
```

The following is valid:
The following would not merge together, however both cannot be encountered
against the same object:

```graphql
fragment mergeSameFieldsWithSameDirectives on Dog {
name @include(if: true)
name @include(if: true)
fragment safeDifferingFields on Pet {
... on Dog {
name: nickname
}
... on Cat {
name
}
}
```

and the following is invalid:

```!graphql
fragment conflictingDirectiveArgs on Dog {
name @include(if: true)
name @include(if: false)
fragment safeDifferingArgs on Pet {
... on Dog {
doesKnowCommand(dogCommand: SIT)
}
... on Cat {
doesKnowCommand(catCommand: JUMP)
}
}
```

Expand Down

0 comments on commit 6a639da

Please sign in to comment.