Skip to content

Commit db956e2

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

File tree

5 files changed

+84
-70
lines changed

5 files changed

+84
-70
lines changed

spec/Appendix B -- Grammar Summary.md

Lines changed: 2 additions & 8 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?
176-
TypeCondition Directives? SelectionSet
175+
FragmentDefinition : fragment FragmentName VariablesDefinition? TypeCondition
176+
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: 10 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -518,8 +518,8 @@ which returns the result:
518518

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

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

524524
FragmentName : Name but not `on`
525525

@@ -1212,21 +1212,15 @@ 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
1216+
referenced in a fragment is included by an operation where neither the fragment
1217+
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
1229-
caller to specify the fragment's behavior.
1222+
Fragments may define locally scoped variables. This allows fragments to be
1223+
reused while enabling the caller to specify the fragment's behavior.
12301224

12311225
For example, the profile picture may need to be a different size depending on
12321226
the parent context:
@@ -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.
1249+
Fragment-defined variables 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: 72 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,8 @@ 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
423+
arguments.
423424
- Let {spreadsForName} be the set of fragment spreads with a given name in
424425
{visitedSelections}.
425426
- Given each pair of members {spreadA} and {spreadB} in {spreadsForName}:
@@ -576,7 +577,7 @@ fragment conflictingDifferingResponses on Pet {
576577
}
577578
```
578579

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

581582
While the following is valid:
582583

@@ -599,11 +600,11 @@ fragment safeFragmentArguments on Dog {
599600
```
600601

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

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

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

707708
- For each {argument} in the document:
708709
- 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}.
710+
- If the parent is a field or directive:
711+
- Let {argumentDefinition} be the argument or variable definition named
712+
{argumentName} provided by the parent field definition, directive definition
713+
or fragment definition.
711714
- {argumentDefinition} must exist.
712715

713716
**Explanatory Text**
714717

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

718721
For example the following are valid:
719722

@@ -744,7 +747,7 @@ fragment invalidArgName on Dog {
744747
}
745748
```
746749

747-
and this is also invalid as the argument `dogCommand` is not defined on fragment
750+
and this is also invalid as the variable `dogCommand` is not defined on fragment
748751
`withFragmentArg`.
749752

750753
```graphql counter-example
@@ -810,10 +813,10 @@ ambiguous and invalid.
810813
#### Required Arguments
811814

812815
- 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.
816+
- Let {arguments} be the arguments provided by the Field, Directive or
817+
Fragment Spread.
818+
- Let {argumentDefinitions} be the set of argument definitions of that Field
819+
or Directive, or the variable definitions of that Fragment.
817820
- For each {argumentDefinition} in {argumentDefinitions}:
818821
- Let {type} be the expected type of {argumentDefinition}.
819822
- Let {defaultValue} be the default value of {argumentDefinition}.
@@ -1592,18 +1595,18 @@ query ($foo: Boolean = true, $bar: Boolean = false) {
15921595

15931596
**Formal Specification**
15941597

1595-
- For every {operation} in the document:
1596-
- For every {variable} defined on {operation}:
1598+
- For every {operation} and {fragment} in the document:
1599+
- For every {variable} defined on that {operation} or {fragment}:
15971600
- Let {variableName} be the name of {variable}.
15981601
- Let {variables} be the set of all variables named {variableName} on
15991602
{operation}.
16001603
- {variables} must be a set of one.
16011604

16021605
**Explanatory Text**
16031606

1604-
If any operation defines more than one variable with the same name, it is
1605-
ambiguous and invalid. It is invalid even if the type of the duplicate variable
1606-
is the same.
1607+
If any operation or fragment defines more than one variable with the same name,
1608+
it is ambiguous and invalid. It is invalid even if the type of the duplicate
1609+
variable is the same.
16071610

16081611
```graphql counter-example
16091612
query houseTrainedQuery($atOtherHomes: Boolean, $atOtherHomes: Boolean) {
@@ -1632,12 +1635,35 @@ fragment HouseTrainedFragment on Query {
16321635
}
16331636
```
16341637

1638+
Likewise, it is valid for both an operation and a fragment to define a variable
1639+
with the same name:
1640+
1641+
```graphql example
1642+
query C($atOtherHomes: Boolean) {
1643+
...HouseTrainedFragment
1644+
aDog: dog {
1645+
...HouseTrainedDog
1646+
}
1647+
}
1648+
1649+
fragment HouseTrainedDog($atOtherHomes: Boolean) on Dog {
1650+
isHouseTrained(atOtherHomes: $atOtherHomes)
1651+
}
1652+
```
1653+
1654+
Fragment-defined variables are scoped locally to the fragment that defines them,
1655+
and override any operation-defined variable values, so there is never ambiguity
1656+
about which value to use. In this case, the value of the argument `atOtherHomes`
1657+
within `HouseTrainedFragment` will be the operation-set value, and within
1658+
`HouseTrainedDog` will resolve to `null`, as the argument is not set by the
1659+
fragment spread in the query `C`.
1660+
16351661
### Variables Are Input Types
16361662

16371663
**Formal Specification**
16381664

1639-
- For every {operation} in a {document}:
1640-
- For every {variable} on each {operation}:
1665+
- For every {operation} and {fragment} in a {document}:
1666+
- For every {variable} defined on each {operation} or {fragment}:
16411667
- Let {variableType} be the type of {variable}.
16421668
- {IsInputType(variableType)} must be {true}.
16431669

@@ -1705,13 +1731,14 @@ query takesCatOrDog($catOrDog: CatOrDog) {
17051731
transitively.
17061732
- For each {fragment} in {fragments}:
17071733
- For each {variableUsage} in scope of {fragment}, variable must be in
1708-
{operation}'s variable list.
1734+
{fragment}'s or {operation}'s variable list.
17091735

17101736
**Explanatory Text**
17111737

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
1738+
Operation-defined Variables are scoped on a per-operation basis, while
1739+
Fragment-defined Variables are scoped locally to the fragment. That means that
1740+
any variable used within the context of an operation must either be defined at
1741+
the top level of that operation or on the fragment that uses that variable.
17151742

17161743
For example:
17171744

@@ -1738,9 +1765,10 @@ query variableIsNotDefined {
17381765
${atOtherHomes} is not defined by the operation.
17391766

17401767
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
1743-
to variable definitions in all of those operations.
1768+
operation has access to the variables defined by that operation and defined on
1769+
the fragment. Fragments can appear within multiple operations and therefore
1770+
variable usages not defined on the fragment must correspond to variable
1771+
definitions in all of those operations.
17441772

17451773
For example the following is valid:
17461774

@@ -1837,7 +1865,7 @@ This is because {houseTrainedQueryTwoNotDefined} does not define a variable
18371865
${atOtherHomes} but that variable is used by {isHouseTrainedFragment} which is
18381866
included in that operation.
18391867

1840-
### All Variables Used
1868+
### All Operation Variables Used
18411869

18421870
**Formal Specification**
18431871

@@ -1945,20 +1973,21 @@ fragment isHouseTrainedFragment on Dog {
19451973
This document is not valid because {queryWithExtraVar} defines an extraneous
19461974
variable.
19471975

1948-
### All Fragment Arguments Used
1976+
### All Fragment Variables Used
19491977

19501978
**Formal Specification**
19511979

19521980
- 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
1981+
- Let {variables} be the variables defined by that {fragment}.
1982+
- Each {variable} in {variables} must be used at least once in the fragment's
19551983
scope.
19561984

19571985
**Explanatory Text**
19581986

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.
1987+
All variables defined by a fragment must be used in that same fragment. Because
1988+
fragment-defined variables are scoped to the fragment they are defined on, if
1989+
the fragment does not use the variable, then the variable definition is
1990+
superfluous.
19621991

19631992
For example, the following is invalid:
19641993

@@ -1974,10 +2003,10 @@ fragment fragmentArgUnused($atOtherHomes: Boolean) on Dog {
19742003
}
19752004
```
19762005

1977-
This document is invalid because even though `fragmentArgUnused` is spread with
1978-
the argument `atOtherHomes`, and even though `$atOtherHomes` is defined as an
1979-
operation variable, there is never a variable `$atOtherHomes` used within the
1980-
scope of `fragmentArgUnused`.
2006+
This document is invalid: even though `fragmentArgUnused` is spread with the
2007+
argument `atOtherHomes` and `$atOtherHomes` is defined as an operation variable,
2008+
there is never a variable `$atOtherHomes` used within the scope of
2009+
`fragmentArgUnused`.
19812010

19822011
### All Variable Usages Are Allowed
19832012

@@ -1987,9 +2016,9 @@ scope of `fragmentArgUnused`.
19872016
- Let {variableUsages} be all usages transitively included in the {operation}.
19882017
- For each {variableUsage} in {variableUsages}:
19892018
- Let {variableName} be the name of {variableUsage}.
1990-
- If the usage is within a {fragment} that defines an argument of
1991-
{variableName}:
1992-
- Let {variableDefinition} be the {ArgumentDefinition} named
2019+
- If the usage is within a {fragment} that defines a {variableDefinition}
2020+
for {variableName}:
2021+
- Let {variableDefinition} be the {VariableDefinition} named
19932022
{variableName} defined within {fragment}.
19942023
- Otherwise, let {variableDefinition} be the {VariableDefinition} named
19952024
{variableName} defined within {operation}.

0 commit comments

Comments
 (0)