Skip to content

Commit 800815d

Browse files
committed
Introduce Strict and Legacy AllVariableUsagesAreAllowed
1 parent 3adfcca commit 800815d

File tree

1 file changed

+181
-19
lines changed

1 file changed

+181
-19
lines changed

spec/Section 5 -- Validation.md

Lines changed: 181 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1855,6 +1855,26 @@ variable.
18551855

18561856
### All Variable Usages Are Allowed
18571857

1858+
As GraphQL evolves and issues are spotted and fixed, the specification aims to
1859+
maintain compatibility with existing GraphQL documents. In rare cases, this
1860+
requires two versions of an algorithm, a Legacy Algorithm which supports clients
1861+
with both old and new GraphQL documents, and a Strict Algorithm which only
1862+
supports new GraphQL documents. It is recommended that you implement the Strict
1863+
Algorithm unless you have existing GraphQL documents that you need to support
1864+
which would become invalid under the Strict Algorithm, in which case you should
1865+
implement the Legacy Algorithm.
1866+
1867+
The All Variable Usages Are Allowed validation rule has become stricter since
1868+
the release of the GraphQL Specification, specifically the Strict Algorithm no
1869+
longer allows a nullable variable to be used in a non-nullable position even
1870+
when either or both of the variable definition and the input position have a
1871+
default value.
1872+
1873+
#### Strict All Variable Usages Are Allowed
1874+
1875+
Implement this only if you are not implementing
1876+
[Legacy All Variable Usages Are Allowed](#sec-Legacy-All-Variable-Usages-Are-Allowed).
1877+
18581878
**Formal Specification**
18591879

18601880
- For each {operation} in {document}:
@@ -1868,6 +1888,144 @@ variable.
18681888

18691889
IsVariableUsageAllowed(variableDefinition, variableUsage):
18701890

1891+
- Let {variableType} be the expected type of {variableDefinition}.
1892+
- Let {locationType} be the expected type of the {Argument}, {ObjectField}, or
1893+
{ListValue} entry where {variableUsage} is located.
1894+
- Return {AreTypesCompatible(variableType, locationType)}.
1895+
1896+
**Explanatory Text**
1897+
1898+
Variable usages must be compatible with the arguments they are passed to.
1899+
1900+
Validation failures occur when variables are used in the context of types that
1901+
are complete mismatches, or if a nullable type in a variable is passed to a
1902+
non-null argument type.
1903+
1904+
Types must match:
1905+
1906+
```graphql counter-example
1907+
query intCannotGoIntoBoolean($intArg: Int) {
1908+
arguments {
1909+
booleanArgField(booleanArg: $intArg)
1910+
}
1911+
}
1912+
```
1913+
1914+
${intArg} typed as {Int} cannot be used as an argument to {booleanArg}, typed as
1915+
{Boolean}.
1916+
1917+
List cardinality must also be the same. For example, lists cannot be passed into
1918+
singular values.
1919+
1920+
```graphql counter-example
1921+
query booleanListCannotGoIntoBoolean($booleanListArg: [Boolean]) {
1922+
arguments {
1923+
booleanArgField(booleanArg: $booleanListArg)
1924+
}
1925+
}
1926+
```
1927+
1928+
Nullability must also be respected. A nullable variable cannot be passed to a
1929+
non-null argument.
1930+
1931+
```graphql counter-example
1932+
query booleanArgQuery($booleanArg: Boolean) {
1933+
arguments {
1934+
nonNullBooleanArgField(nonNullBooleanArg: $booleanArg)
1935+
}
1936+
}
1937+
```
1938+
1939+
For list types, the same rules around nullability apply to both outer types and
1940+
inner types. A nullable list cannot be passed to a non-null list, and a list of
1941+
nullable values cannot be passed to a list of non-null values. The following is
1942+
valid:
1943+
1944+
```graphql example
1945+
query nonNullListToList($nonNullBooleanList: [Boolean]!) {
1946+
arguments {
1947+
booleanListArgField(booleanListArg: $nonNullBooleanList)
1948+
}
1949+
}
1950+
```
1951+
1952+
However, a nullable list cannot be passed to a non-null list:
1953+
1954+
```graphql counter-example
1955+
query listToNonNullList($booleanList: [Boolean]) {
1956+
arguments {
1957+
nonNullBooleanListField(nonNullBooleanListArg: $booleanList)
1958+
}
1959+
}
1960+
```
1961+
1962+
This would fail validation because a `[T]` cannot be passed to a `[T]!`.
1963+
Similarly a `[T]` cannot be passed to a `[T!]`.
1964+
1965+
**Nullability, Optionality, and Default Values**
1966+
1967+
The value for a non-nullable variable with a default value may be omitted, or
1968+
may be set to a non-null value, but it cannot be explicitly {null}. Thus, rather
1969+
than handling a {null} with a run-time _field error_ as in Legacy All Variable
1970+
Usages Are Allowed, in Strict All Variable Usages Are Allowed we can make this
1971+
situation impossible via validation.
1972+
1973+
In the example below, the variable `$booleanArg` must be non-null because it is
1974+
used in the non-null argument (`nonNullBooleanArg`); however since it provides a
1975+
default value the variable need not be defined in the request (it is optional,
1976+
but non-nullable).
1977+
1978+
```graphql example
1979+
query booleanArgQueryWithDefault($booleanArg: Boolean! = true) {
1980+
arguments {
1981+
nonNullBooleanArgField(nonNullBooleanArg: $booleanArg)
1982+
}
1983+
}
1984+
```
1985+
1986+
In the example below, the variable `$booleanArg` must be non-nullable since it
1987+
is used in a non-null position, even though the argument `optionalBooleanArg`
1988+
has a default value.
1989+
1990+
```graphql example
1991+
query booleanArgQueryWithDefault($booleanArg: Boolean!) {
1992+
arguments {
1993+
optionalNonNullBooleanArgField(optionalBooleanArg: $booleanArg)
1994+
}
1995+
}
1996+
```
1997+
1998+
The default value of `optionalBooleanArg` cannot be used when the argument is
1999+
specified (either with a literal or a variable). You may choose to copy the
2000+
argument's default value to the variable definition to make the variable
2001+
optional (but still non-nullable) as in the example below:
2002+
2003+
```graphql example
2004+
query booleanArgQueryWithDefault($booleanArg: Boolean! = false) {
2005+
arguments {
2006+
optionalNonNullBooleanArgField(optionalBooleanArg: $booleanArg)
2007+
}
2008+
}
2009+
```
2010+
2011+
#### Legacy All Variable Usages Are Allowed
2012+
2013+
Implement this only if you are not implementing
2014+
[Strict All Variable Usages Are Allowed](#sec-Strict-All-Variable-Usages-Are-Allowed).
2015+
2016+
**Formal Specification**
2017+
2018+
- For each {operation} in {document}:
2019+
- Let {variableUsages} be all usages transitively included in the {operation}.
2020+
- For each {variableUsage} in {variableUsages}:
2021+
- Let {variableName} be the name of {variableUsage}.
2022+
- Let {variableDefinition} be the {VariableDefinition} named {variableName}
2023+
defined within {operation}.
2024+
- {IsVariableUsageAllowedLegacy(variableDefinition, variableUsage)} must be
2025+
{true}.
2026+
2027+
IsVariableUsageAllowedLegacy(variableDefinition, variableUsage):
2028+
18712029
- Let {variableType} be the expected type of {variableDefinition}.
18722030
- Let {locationType} be the expected type of the {Argument}, {ObjectField}, or
18732031
{ListValue} entry where {variableUsage} is located.
@@ -1883,25 +2041,6 @@ IsVariableUsageAllowed(variableDefinition, variableUsage):
18832041
- Return {AreTypesCompatible(variableType, nullableLocationType)}.
18842042
- Return {AreTypesCompatible(variableType, locationType)}.
18852043

1886-
AreTypesCompatible(variableType, locationType):
1887-
1888-
- If {locationType} is a non-null type:
1889-
- If {variableType} is NOT a non-null type, return {false}.
1890-
- Let {nullableLocationType} be the unwrapped nullable type of {locationType}.
1891-
- Let {nullableVariableType} be the unwrapped nullable type of {variableType}.
1892-
- Return {AreTypesCompatible(nullableVariableType, nullableLocationType)}.
1893-
- Otherwise, if {variableType} is a non-null type:
1894-
- Let {nullableVariableType} be the nullable type of {variableType}.
1895-
- Return {AreTypesCompatible(nullableVariableType, locationType)}.
1896-
- Otherwise, if {locationType} is a list type:
1897-
- If {variableType} is NOT a list type, return {false}.
1898-
- Let {itemLocationType} be the unwrapped item type of {locationType}.
1899-
- Let {itemVariableType} be the unwrapped item type of {variableType}.
1900-
- Return {AreTypesCompatible(itemVariableType, itemLocationType)}.
1901-
- Otherwise, if {variableType} is a list type, return {false}.
1902-
- Return {true} if {variableType} and {locationType} are identical, otherwise
1903-
{false}.
1904-
19052044
**Explanatory Text**
19062045

19072046
Variable usages must be compatible with the arguments they are passed to.
@@ -2006,3 +2145,26 @@ query booleanArgQueryWithDefault($booleanArg: Boolean = true) {
20062145

20072146
Note: The value {null} could still be provided to such a variable at runtime. A
20082147
non-null argument must raise a _field error_ if provided a {null} value.
2148+
2149+
#### Are Types Compatible
2150+
2151+
Both versions of the algorithm share the definition of {AreTypesCompatible()}:
2152+
2153+
AreTypesCompatible(variableType, locationType):
2154+
2155+
- If {locationType} is a non-null type:
2156+
- If {variableType} is NOT a non-null type, return {false}.
2157+
- Let {nullableLocationType} be the unwrapped nullable type of {locationType}.
2158+
- Let {nullableVariableType} be the unwrapped nullable type of {variableType}.
2159+
- Return {AreTypesCompatible(nullableVariableType, nullableLocationType)}.
2160+
- Otherwise, if {variableType} is a non-null type:
2161+
- Let {nullableVariableType} be the nullable type of {variableType}.
2162+
- Return {AreTypesCompatible(nullableVariableType, locationType)}.
2163+
- Otherwise, if {locationType} is a list type:
2164+
- If {variableType} is NOT a list type, return {false}.
2165+
- Let {itemLocationType} be the unwrapped item type of {locationType}.
2166+
- Let {itemVariableType} be the unwrapped item type of {variableType}.
2167+
- Return {AreTypesCompatible(itemVariableType, itemLocationType)}.
2168+
- Otherwise, if {variableType} is a list type, return {false}.
2169+
- Return {true} if {variableType} and {locationType} are identical, otherwise
2170+
{false}.

0 commit comments

Comments
 (0)