Skip to content
Open
Show file tree
Hide file tree
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
10 changes: 2 additions & 8 deletions spec/Appendix B -- Grammar Summary.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,16 +172,11 @@ FragmentSpread : ... FragmentName Arguments? Directives?

InlineFragment : ... TypeCondition? Directives? SelectionSet

FragmentDefinition : fragment FragmentName FragmentArgumentsDefinition?
TypeCondition Directives? SelectionSet
FragmentDefinition : fragment FragmentName VariablesDefinition? TypeCondition
Directives? SelectionSet

FragmentName : Name but not `on`

FragmentArgumentsDefinition : ( FragmentArgumentDefinition+ )

FragmentArgumentDefinition : Description? Variable : Type DefaultValue?
Directives[Const]?

TypeCondition : on NamedType

Value[Const] :
Expand Down Expand Up @@ -401,7 +396,6 @@ ExecutableDirectiveLocation : one of
- `FRAGMENT_SPREAD`
- `INLINE_FRAGMENT`
- `VARIABLE_DEFINITION`
- `FRAGMENT_ARGUMENT_DEFINITION`

TypeSystemDirectiveLocation : one of

Expand Down
26 changes: 10 additions & 16 deletions spec/Section 2 -- Language.md
Original file line number Diff line number Diff line change
Expand Up @@ -518,8 +518,8 @@ which returns the result:

FragmentSpread : ... FragmentName Arguments? Directives?

FragmentDefinition : fragment FragmentName FragmentArgumentsDefinition?
TypeCondition Directives? SelectionSet
FragmentDefinition : fragment FragmentName VariablesDefinition? TypeCondition
Directives? SelectionSet

FragmentName : Name but not `on`

Expand Down Expand Up @@ -1212,21 +1212,15 @@ size `60`:
Variables can be used within fragments. Operation-defined variables have global
scope with a given operation, so a variable used within a fragment must either
be declared in any top-level operation that transitively consumes that fragment,
or by that same fragment as a fragment argument. If a variable is referenced in
a fragment that does not define it as an argument and is included by an
operation that does not define that variable, that operation is invalid (see
or by that same fragment as a fragment variable definition. If a variable is
referenced in a fragment is included by an operation where neither the fragment
nor the operaiton defines that variable, that operation is invalid (see
[All Variable Uses Defined](#sec-All-Variable-Uses-Defined)).

## Fragment Arguments
## Fragment Variable Definitions

FragmentArgumentsDefinition : ( FragmentArgumentDefinition+ )

FragmentArgumentDefinition : Description? Variable : Type DefaultValue?
Directives[Const]?

Fragments may define locally scoped arguments, which can be used in locations
that accept variables. This allows fragments to be reused while enabling the
caller to specify the fragment's behavior.
Fragments may define locally scoped variables. This allows fragments to be
reused while enabling the caller to specify the fragment's behavior.

For example, the profile picture may need to be a different size depending on
the parent context:
Expand All @@ -1251,8 +1245,8 @@ fragment dynamicProfilePic($size: Int! = 50) on User {
In this case the `user` will have a larger `profilePic` than those found in the
list of `friends`.

A fragment argument is scoped to the fragment that defines it. Fragment
arguments are allowed to shadow operation variables.
A fragment-defined variable is scoped to the fragment that defines it.
Fragment-defined variables are allowed to shadow operation-defined variables.

```graphql example
query withShadowedVariables($size: Int) {
Expand Down
1 change: 0 additions & 1 deletion spec/Section 3 -- Type System.md
Original file line number Diff line number Diff line change
Expand Up @@ -1876,7 +1876,6 @@ ExecutableDirectiveLocation : one of
- `FRAGMENT_SPREAD`
- `INLINE_FRAGMENT`
- `VARIABLE_DEFINITION`
- `FRAGMENT_ARGUMENT_DEFINITION`

TypeSystemDirectiveLocation : one of

Expand Down
2 changes: 0 additions & 2 deletions spec/Section 4 -- Introspection.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,6 @@ enum __DirectiveLocation {
FRAGMENT_SPREAD
INLINE_FRAGMENT
VARIABLE_DEFINITION
FRAGMENT_ARGUMENT_DEFINITION
SCHEMA
SCALAR
OBJECT
Expand Down Expand Up @@ -476,7 +475,6 @@ supported. All possible locations are listed in the `__DirectiveLocation` enum:
- {"FRAGMENT_SPREAD"}
- {"INLINE_FRAGMENT"}
- {"VARIABLE_DEFINITION"}
- {"FRAGMENT_ARGUMENT_DEFINITION"}
- {"SCHEMA"}
- {"SCALAR"}
- {"OBJECT"}
Expand Down
119 changes: 75 additions & 44 deletions spec/Section 5 -- Validation.md
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,8 @@ fragment directFieldSelectionOnUnion on CatOrDog {
FieldsInSetCanMerge(set):

- Let {visitedSelections} be the selections in {set} including visiting
fragments and inline fragments an applying any supplied fragment arguments.
fragments and inline fragments and applying any supplied fragment spread
arguments.
- Let {spreadsForName} be the set of fragment spreads with a given name in
{visitedSelections}.
- Given each pair of members {spreadA} and {spreadB} in {spreadsForName}:
Expand Down Expand Up @@ -576,7 +577,7 @@ fragment conflictingDifferingResponses on Pet {
}
```

Fragment arguments can also cause fields to fail to merge.
Fragment spread arguments can also cause fields to fail to merge.

While the following is valid:

Expand All @@ -599,11 +600,11 @@ fragment safeFragmentArguments on Dog {
```

it is only valid because `safeFragmentArguments` uses
`potentiallyConflictingArguments` with the same value for `commandOne` and
`commandTwo`. Therefore `commandFragment` resolves `doesKnowCommand`'s
`dogCommand:` arg to `SIT` in both cases.
`potentiallyConflictingArguments` with the same value for the fragment-defined
variables `commandOne` and `commandTwo`. Therefore `commandFragment` resolves
`doesKnowCommand`'s `dogCommand` argument value to `SIT` in both cases.

However, by changing the argument values:
However, by changing the fragment spread argument values:

```graphql counter-example
fragment conflictingFragmentArguments on Dog {
Expand Down Expand Up @@ -706,14 +707,16 @@ validation rules apply in each case.

- For each {argument} in the document:
- Let {argumentName} be the Name of {argument}.
- Let {argumentDefinition} be the argument definition provided by the parent
field, fragment definition or directive definition named {argumentName}.
- If the parent is a field or directive:
- Let {argumentDefinition} be the argument or variable definition named
{argumentName} provided by the parent field definition, directive definition
or fragment definition.
- {argumentDefinition} must exist.

**Explanatory Text**

Every argument provided to a field or directive must be defined in the set of
possible arguments of that field or directive.
Every argument provided to a field or directive or fragment spread must be
defined in the set of possible arguments of that field, directive or fragment.

For example the following are valid:

Expand Down Expand Up @@ -744,7 +747,7 @@ fragment invalidArgName on Dog {
}
```

and this is also invalid as the argument `dogCommand` is not defined on fragment
and this is also invalid as the variable `dogCommand` is not defined on fragment
`withFragmentArg`.

```graphql counter-example
Expand Down Expand Up @@ -810,10 +813,10 @@ ambiguous and invalid.
#### Required Arguments

- For each Field, Fragment Spread or Directive in the document:
- Let {arguments} be the arguments provided by the Field, Fragment Spread or
Directive.
- Let {argumentDefinitions} be the set of argument definitions of that Field,
Fragment Spread or Directive.
- Let {arguments} be the arguments provided by the Field, Directive or
Fragment Spread.
- Let {argumentDefinitions} be the set of argument definitions of that Field
or Directive, or the variable definitions of that Fragment.
- For each {argumentDefinition} in {argumentDefinitions}:
- Let {type} be the expected type of {argumentDefinition}.
- Let {defaultValue} be the default value of {argumentDefinition}.
Expand Down Expand Up @@ -1592,18 +1595,19 @@ query ($foo: Boolean = true, $bar: Boolean = false) {

**Formal Specification**

- For every {operation} in the document:
- For every {variable} defined on {operation}:
- For every {operation} and {fragment} in the document:
- Let {operationOrFragment} be that {operation} or {fragment}.
- For every {variable} defined on {operationOrFragment}:
- Let {variableName} be the name of {variable}.
- Let {variables} be the set of all variables named {variableName} on
{operation}.
{operationOrFragment}.
- {variables} must be a set of one.

**Explanatory Text**

If any operation defines more than one variable with the same name, it is
ambiguous and invalid. It is invalid even if the type of the duplicate variable
is the same.
If any operation or fragment defines more than one variable with the same name,
it is ambiguous and invalid. It is invalid even if the type of the duplicate
variable is the same.

```graphql counter-example
query houseTrainedQuery($atOtherHomes: Boolean, $atOtherHomes: Boolean) {
Expand Down Expand Up @@ -1632,12 +1636,36 @@ fragment HouseTrainedFragment on Query {
}
```

Likewise, it is valid for both an operation and a fragment to define a variable
with the same name:

```graphql example
query C($atOtherHomes: Boolean) {
...HouseTrainedFragment
aDog: dog {
...HouseTrainedDog
}
}

fragment HouseTrainedDog($atOtherHomes: Boolean) on Dog {
isHouseTrained(atOtherHomes: $atOtherHomes)
}
```

Fragment-defined variables are scoped locally to the fragment that defines them,
and override any operation-defined variable values, so there is never ambiguity
about which value to use. In this case, the value of the argument `atOtherHomes`
within `HouseTrainedFragment` will be the operation-set value, and within
`HouseTrainedDog` will resolve to `null`, as the argument is not set by the
fragment spread in the query `C`.

### Variables Are Input Types

**Formal Specification**

- For every {operation} in a {document}:
- For every {variable} on each {operation}:
- For every {operation} and {fragment} in a {document}:
- Let {operationOrFragment} be that {operation} or {fragment}.
- For every {variable} defined on {operationOrFragment}:
- Let {variableType} be the type of {variable}.
- {IsInputType(variableType)} must be {true}.

Expand Down Expand Up @@ -1705,13 +1733,14 @@ query takesCatOrDog($catOrDog: CatOrDog) {
transitively.
- For each {fragment} in {fragments}:
- For each {variableUsage} in scope of {fragment}, variable must be in
{operation}'s variable list.
{fragment}'s or {operation}'s variable list.

**Explanatory Text**

Variables are scoped on a per-operation basis. That means that any variable used
within the context of an operation must be defined at the top level of that
operation
Operation-defined Variables are scoped on a per-operation basis, while
Fragment-defined Variables are scoped locally to the fragment. That means that
any variable used within the context of an operation must either be defined at
the top level of that operation or on the fragment that uses that variable.

For example:

Expand All @@ -1738,9 +1767,10 @@ query variableIsNotDefined {
${atOtherHomes} is not defined by the operation.

Fragments complicate this rule. Any fragment transitively included by an
operation has access to the variables defined by that operation. Fragments can
appear within multiple operations and therefore variable usages must correspond
to variable definitions in all of those operations.
operation has access to the variables defined by that operation and defined on
the fragment. Fragments can appear within multiple operations and therefore
variable usages not defined on the fragment must correspond to variable
definitions in all of those operations.

For example the following is valid:

Expand Down Expand Up @@ -1837,7 +1867,7 @@ This is because {houseTrainedQueryTwoNotDefined} does not define a variable
${atOtherHomes} but that variable is used by {isHouseTrainedFragment} which is
included in that operation.

### All Variables Used
### All Operation Variables Used

**Formal Specification**

Expand Down Expand Up @@ -1945,20 +1975,21 @@ fragment isHouseTrainedFragment on Dog {
This document is not valid because {queryWithExtraVar} defines an extraneous
variable.

### All Fragment Arguments Used
### All Fragment Variables Used

**Formal Specification**

- For every {fragment} in the document:
- Let {arguments} be the arguments defined by that {fragment}.
- Each {argument} in {arguments} must be used at least once in the fragment's
- Let {variables} be the variables defined by that {fragment}.
- Each {variable} in {variables} must be used at least once in the fragment's
scope.

**Explanatory Text**

All arguments defined by a fragment must be used in that same fragment. Because
fragment arguments are scoped to the fragment they are defined on, if the
fragment does not use the argument, then the argument is superfluous.
All variables defined by a fragment must be used in that same fragment. Because
fragment-defined variables are scoped to the fragment they are defined on, if
the fragment does not use the variable, then the variable definition is
superfluous.

For example, the following is invalid:

Expand All @@ -1974,10 +2005,10 @@ fragment fragmentArgUnused($atOtherHomes: Boolean) on Dog {
}
```

This document is invalid because even though `fragmentArgUnused` is spread with
the argument `atOtherHomes`, and even though `$atOtherHomes` is defined as an
operation variable, there is never a variable `$atOtherHomes` used within the
scope of `fragmentArgUnused`.
This document is invalid: even though `fragmentArgUnused` is spread with the
argument `atOtherHomes` and `$atOtherHomes` is defined as an operation variable,
there is never a variable `$atOtherHomes` used within the scope of
`fragmentArgUnused`.

### All Variable Usages Are Allowed

Expand All @@ -1987,9 +2018,9 @@ scope of `fragmentArgUnused`.
- Let {variableUsages} be all usages transitively included in the {operation}.
- For each {variableUsage} in {variableUsages}:
- Let {variableName} be the name of {variableUsage}.
- If the usage is within a {fragment} that defines an argument of
{variableName}:
- Let {variableDefinition} be the {ArgumentDefinition} named
- If the usage is within a {fragment} that defines a {variableDefinition}
for {variableName}:
- Let {variableDefinition} be the {VariableDefinition} named
{variableName} defined within {fragment}.
- Otherwise, let {variableDefinition} be the {VariableDefinition} named
{variableName} defined within {operation}.
Expand Down
Loading