Skip to content

Commit 178b63a

Browse files
committed
RFC: Fragment Args defined as Variable Definition
1 parent c988b54 commit 178b63a

File tree

5 files changed

+61
-60
lines changed

5 files changed

+61
-60
lines changed

spec/Appendix B -- Grammar Summary.md

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -172,16 +172,11 @@ FragmentSpread : ... FragmentName Arguments? Directives?
172172

173173
InlineFragment : ... TypeCondition? Directives? SelectionSet
174174

175-
FragmentDefinition : fragment FragmentName FragmentArgumentsDefinition?
175+
FragmentDefinition : fragment FragmentName VariablesDefinition?
176176
TypeCondition Directives? SelectionSet
177177

178178
FragmentName : Name but not `on`
179179

180-
FragmentArgumentsDefinition : ( FragmentArgumentDefinition+ )
181-
182-
FragmentArgumentDefinition : Description? Variable : Type DefaultValue?
183-
Directives[Const]?
184-
185180
TypeCondition : on NamedType
186181

187182
Value[Const] :
@@ -401,7 +396,6 @@ ExecutableDirectiveLocation : one of
401396
- `FRAGMENT_SPREAD`
402397
- `INLINE_FRAGMENT`
403398
- `VARIABLE_DEFINITION`
404-
- `FRAGMENT_ARGUMENT_DEFINITION`
405399

406400
TypeSystemDirectiveLocation : one of
407401

spec/Section 2 -- Language.md

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ which returns the result:
518518

519519
FragmentSpread : ... FragmentName Arguments? Directives?
520520

521-
FragmentDefinition : fragment FragmentName FragmentArgumentsDefinition?
521+
FragmentDefinition : fragment FragmentName VariablesDefinition?
522522
TypeCondition Directives? SelectionSet
523523

524524
FragmentName : Name but not `on`
@@ -1212,20 +1212,14 @@ size `60`:
12121212
Variables can be used within fragments. Operation-defined variables have global
12131213
scope with a given operation, so a variable used within a fragment must either
12141214
be declared in any top-level operation that transitively consumes that fragment,
1215-
or by that same fragment as a fragment argument. If a variable is referenced in
1216-
a fragment that does not define it as an argument and is included by an
1217-
operation that does not define that variable, that operation is invalid (see
1215+
or by that same fragment as a fragment variable definition. If a variable is referenced in
1216+
a fragment is included by an
1217+
operation where neither the fragment nor the operaiton defines that variable, that operation is invalid (see
12181218
[All Variable Uses Defined](#sec-All-Variable-Uses-Defined)).
12191219

1220-
## Fragment Arguments
1220+
## Fragment Variable Definitions
12211221

1222-
FragmentArgumentsDefinition : ( FragmentArgumentDefinition+ )
1223-
1224-
FragmentArgumentDefinition : Description? Variable : Type DefaultValue?
1225-
Directives[Const]?
1226-
1227-
Fragments may define locally scoped arguments, which can be used in locations
1228-
that accept variables. This allows fragments to be reused while enabling the
1222+
Fragments may define locally scoped variables. This allows fragments to be reused while enabling the
12291223
caller to specify the fragment's behavior.
12301224

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

1254-
A fragment argument is scoped to the fragment that defines it. Fragment
1255-
arguments are allowed to shadow operation variables.
1248+
A fragment-defined variable is scoped to the fragment that defines it. Fragment-defined variables
1249+
are allowed to shadow operation-defined variables.
12561250

12571251
```graphql example
12581252
query withShadowedVariables($size: Int) {

spec/Section 3 -- Type System.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1876,7 +1876,6 @@ ExecutableDirectiveLocation : one of
18761876
- `FRAGMENT_SPREAD`
18771877
- `INLINE_FRAGMENT`
18781878
- `VARIABLE_DEFINITION`
1879-
- `FRAGMENT_ARGUMENT_DEFINITION`
18801879

18811880
TypeSystemDirectiveLocation : one of
18821881

spec/Section 4 -- Introspection.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,6 @@ enum __DirectiveLocation {
206206
FRAGMENT_SPREAD
207207
INLINE_FRAGMENT
208208
VARIABLE_DEFINITION
209-
FRAGMENT_ARGUMENT_DEFINITION
210209
SCHEMA
211210
SCALAR
212211
OBJECT
@@ -476,7 +475,6 @@ supported. All possible locations are listed in the `__DirectiveLocation` enum:
476475
- {"FRAGMENT_SPREAD"}
477476
- {"INLINE_FRAGMENT"}
478477
- {"VARIABLE_DEFINITION"}
479-
- {"FRAGMENT_ARGUMENT_DEFINITION"}
480478
- {"SCHEMA"}
481479
- {"SCALAR"}
482480
- {"OBJECT"}

spec/Section 5 -- Validation.md

Lines changed: 52 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@ fragment directFieldSelectionOnUnion on CatOrDog {
419419
FieldsInSetCanMerge(set):
420420

421421
- Let {visitedSelections} be the selections in {set} including visiting
422-
fragments and inline fragments an applying any supplied fragment arguments.
422+
fragments and inline fragments and applying any supplied fragment spread arguments.
423423
- Let {spreadsForName} be the set of fragment spreads with a given name in
424424
{visitedSelections}.
425425
- Given each pair of members {spreadA} and {spreadB} in {spreadsForName}:
@@ -576,7 +576,7 @@ fragment conflictingDifferingResponses on Pet {
576576
}
577577
```
578578

579-
Fragment arguments can also cause fields to fail to merge.
579+
Fragment spread arguments can also cause fields to fail to merge.
580580

581581
While the following is valid:
582582

@@ -599,11 +599,11 @@ fragment safeFragmentArguments on Dog {
599599
```
600600

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

606-
However, by changing the argument values:
606+
However, by changing the fragment spread argument values:
607607

608608
```graphql counter-example
609609
fragment conflictingFragmentArguments on Dog {
@@ -706,14 +706,15 @@ validation rules apply in each case.
706706

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

713714
**Explanatory Text**
714715

715-
Every argument provided to a field or directive must be defined in the set of
716-
possible arguments of that field or directive.
716+
Every argument provided to a field or directive or fragment spread must be defined in the set of
717+
possible arguments of that field, directive or fragment.
717718

718719
For example the following are valid:
719720

@@ -744,7 +745,7 @@ fragment invalidArgName on Dog {
744745
}
745746
```
746747

747-
and this is also invalid as the argument `dogCommand` is not defined on fragment
748+
and this is also invalid as the variable `dogCommand` is not defined on fragment
748749
`withFragmentArg`.
749750

750751
```graphql counter-example
@@ -810,10 +811,9 @@ ambiguous and invalid.
810811
#### Required Arguments
811812

812813
- For each Field, Fragment Spread or Directive in the document:
813-
- Let {arguments} be the arguments provided by the Field, Fragment Spread or
814-
Directive.
815-
- Let {argumentDefinitions} be the set of argument definitions of that Field,
816-
Fragment Spread or Directive.
814+
- Let {arguments} be the arguments provided by the Field,
815+
Directive or Fragment Spread.
816+
- Let {argumentDefinitions} be the set of argument definitions of that Field or Directive, or the variable definitions of that Fragment.
817817
- For each {argumentDefinition} in {argumentDefinitions}:
818818
- Let {type} be the expected type of {argumentDefinition}.
819819
- Let {defaultValue} be the default value of {argumentDefinition}.
@@ -1592,16 +1592,16 @@ query ($foo: Boolean = true, $bar: Boolean = false) {
15921592

15931593
**Formal Specification**
15941594

1595-
- For every {operation} in the document:
1596-
- For every {variable} defined on {operation}:
1595+
- For every {operation} and {fragment} in the document:
1596+
- For every {variable} defined on that {operation} or {fragment}:
15971597
- Let {variableName} be the name of {variable}.
15981598
- Let {variables} be the set of all variables named {variableName} on
15991599
{operation}.
16001600
- {variables} must be a set of one.
16011601

16021602
**Explanatory Text**
16031603

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

@@ -1632,12 +1632,28 @@ fragment HouseTrainedFragment on Query {
16321632
}
16331633
```
16341634

1635+
Likewise, it is valid for both an operation and a fragment to define a variable with the same name:
1636+
```graphql example
1637+
query C($atOtherHomes: Boolean) {
1638+
...HouseTrainedFragment
1639+
aDog: dog {
1640+
...HouseTrainedDog
1641+
}
1642+
}
1643+
1644+
fragment HouseTrainedDog($atOtherHomes: Boolean) on Dog {
1645+
isHouseTrained(atOtherHomes: $atOtherHomes)
1646+
}
1647+
```
1648+
1649+
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`.
1650+
16351651
### Variables Are Input Types
16361652

16371653
**Formal Specification**
16381654

1639-
- For every {operation} in a {document}:
1640-
- For every {variable} on each {operation}:
1655+
- For every {operation} and {fragment} in a {document}:
1656+
- For every {variable} defined on each {operation} or {fragment}:
16411657
- Let {variableType} be the type of {variable}.
16421658
- {IsInputType(variableType)} must be {true}.
16431659

@@ -1704,14 +1720,14 @@ query takesCatOrDog($catOrDog: CatOrDog) {
17041720
- Let {fragments} be every fragment referenced by that {operation}
17051721
transitively.
17061722
- For each {fragment} in {fragments}:
1707-
- For each {variableUsage} in scope of {fragment}, variable must be in
1723+
- For each {variableUsage} in scope of {fragment}, variable must be in {fragment}'s or
17081724
{operation}'s variable list.
17091725

17101726
**Explanatory Text**
17111727

1712-
Variables are scoped on a per-operation basis. That means that any variable used
1713-
within the context of an operation must be defined at the top level of that
1714-
operation
1728+
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
1729+
within the context of an operation must either be defined at the top level of that
1730+
operation or on the fragment that uses that variable.
17151731

17161732
For example:
17171733

@@ -1738,8 +1754,8 @@ query variableIsNotDefined {
17381754
${atOtherHomes} is not defined by the operation.
17391755

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

17451761
For example the following is valid:
@@ -1837,7 +1853,7 @@ This is because {houseTrainedQueryTwoNotDefined} does not define a variable
18371853
${atOtherHomes} but that variable is used by {isHouseTrainedFragment} which is
18381854
included in that operation.
18391855

1840-
### All Variables Used
1856+
### All Operation Variables Used
18411857

18421858
**Formal Specification**
18431859

@@ -1945,20 +1961,20 @@ fragment isHouseTrainedFragment on Dog {
19451961
This document is not valid because {queryWithExtraVar} defines an extraneous
19461962
variable.
19471963

1948-
### All Fragment Arguments Used
1964+
### All Fragment Variables Used
19491965

19501966
**Formal Specification**
19511967

19521968
- For every {fragment} in the document:
1953-
- Let {arguments} be the arguments defined by that {fragment}.
1954-
- Each {argument} in {arguments} must be used at least once in the fragment's
1969+
- Let {variables} be the variables defined by that {fragment}.
1970+
- Each {variable} in {variables} must be used at least once in the fragment's
19551971
scope.
19561972

19571973
**Explanatory Text**
19581974

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

19631979
For example, the following is invalid:
19641980

@@ -1974,8 +1990,8 @@ fragment fragmentArgUnused($atOtherHomes: Boolean) on Dog {
19741990
}
19751991
```
19761992

1977-
This document is invalid because even though `fragmentArgUnused` is spread with
1978-
the argument `atOtherHomes`, and even though `$atOtherHomes` is defined as an
1993+
This document is invalid: even though `fragmentArgUnused` is spread with
1994+
the argument `atOtherHomes` and `$atOtherHomes` is defined as an
19791995
operation variable, there is never a variable `$atOtherHomes` used within the
19801996
scope of `fragmentArgUnused`.
19811997

@@ -1987,9 +2003,9 @@ scope of `fragmentArgUnused`.
19872003
- Let {variableUsages} be all usages transitively included in the {operation}.
19882004
- For each {variableUsage} in {variableUsages}:
19892005
- Let {variableName} be the name of {variableUsage}.
1990-
- If the usage is within a {fragment} that defines an argument of
2006+
- If the usage is within a {fragment} that defines a {variableDefinition} for
19912007
{variableName}:
1992-
- Let {variableDefinition} be the {ArgumentDefinition} named
2008+
- Let {variableDefinition} be the {VariableDefinition} named
19932009
{variableName} defined within {fragment}.
19942010
- Otherwise, let {variableDefinition} be the {VariableDefinition} named
19952011
{variableName} defined within {operation}.

0 commit comments

Comments
 (0)