Description
Consider the following currently valid schema and query:
type Query {
sum(numbers:[Int!]!): Int
}
query Q ($number: Int = 3) {
sum(numbers: [1, $number, 3])
}
With the following variables:
{
"number": null
}
Now following through 6.1.2 Coercing Variable Values:
- Let coercedValues be an empty unordered Map. ✅
- Let variablesDefinition be the variables defined by operation. ✅
- For each variableDefinition in variablesDefinition: ✅
- Let variableName be the name of variableDefinition. ✅
- Let variableType be the expected type of variableDefinition. ✅
- Assert: IsInputType(variableType) must be true. ✅
- Let defaultValue be the default value for variableDefinition. ✅
- Let hasValue be true if variableValues provides a value for the name variableName. ✅
- Let value be the value provided in variableValues for the name variableName. ✅
- If hasValue is not true and defaultValue exists (including null):
- Add an entry to coercedValues named variableName with the value defaultValue.
- Otherwise if variableType is a Non-Nullable type, and either hasValue is not true or value is null, raise a request error.
- Otherwise if hasValue is true: ✅
- If value is null: ✅
- Add an entry to coercedValues named variableName with the value null. ✅
- If value is null: ✅
For the variable definition $number: Int = 3
:
variableType
isInt
(NOTInt!
).defaultValue
is3
hasValue
istrue
sincenumber
is provided in variables (even though it's null)value
isnull
Thus coercedValues
becomes { number: null }
.
When it comes to executing the field, this results in CoerceArgumentValues() raising a field error at 5.j.iii.1 (since [1, null , 3]
cannot be coerced to [Int!]!
).
Had the query have been defined with ($number: Int! = 3)
then this error could not have occurred; but we explicitly allow the nullable Int with default in IsVariableUsageAllowed(). Presumably this is so that we can allow a default value to apply in the list, except this is a ListValue
so there cannot be a default value there.
We've discussed "optionality" versus "nullability" a few times, but I'd like to get the WG's read on this.
Reproduction:
const { graphqlSync, GraphQLSchema, GraphQLList, GraphQLNonNull, GraphQLInt, GraphQLObjectType, validateSchema } = require('graphql');
const Query = new GraphQLObjectType({
name: 'Query',
fields: {
sum: {
type: GraphQLInt,
args: {
numbers: {
type: new GraphQLNonNull(new GraphQLList(new GraphQLNonNull(GraphQLInt))),
}
},
resolve(_, args) {
let total = 0;
for (const n of args.numbers) {
total += n;
}
return total;
}
}
}
});
const schema = new GraphQLSchema({
query: Query
});
const errors = validateSchema(schema);
if (errors.length > 0) {
throw errors[0];
}
{
console.log('Testing query with variables and null');
const result = graphqlSync({
schema,
source: `query Q($number: Int = 3) {sum(numbers:[1,$number,3])}`,
variableValues: {
number: null
}
});
console.dir(result);
}