diff --git a/spec/Appendix B -- Grammar Summary.md b/spec/Appendix B -- Grammar Summary.md index 1cccaaf3d..b1a79f703 100644 --- a/spec/Appendix B -- Grammar Summary.md +++ b/spec/Appendix B -- Grammar Summary.md @@ -127,12 +127,15 @@ Value[Const] : - FloatValue - StringValue - BooleanValue + - NullValue - EnumValue - ListValue[?Const] - ObjectValue[?Const] BooleanValue : one of `true` `false` +NullValue : `null` + EnumValue : Name but not `true`, `false` or `null` ListValue[Const] : diff --git a/spec/Section 2 -- Language.md b/spec/Section 2 -- Language.md index f61cdb76a..892c4a358 100644 --- a/spec/Section 2 -- Language.md +++ b/spec/Section 2 -- Language.md @@ -634,6 +634,7 @@ Value[Const] : - FloatValue - StringValue - BooleanValue + - NullValue - EnumValue - ListValue[?Const] - ObjectValue[?Const] @@ -736,6 +737,37 @@ StringCharacter :: \ EscapedCharacter * Return the character value of {EscapedCharacter}. +### Null Value + +NullValue : `null` + +Null values are represented as the keyword {null}. + +GraphQL has two semantically different ways to represent the lack of a value: + + * Explicitly providing the literal value: {null}. + * Implicitly not providing a value at all. + +For example, these two field calls are similar, but are not identical: + +```graphql +{ + field(arg: null) + field +} +``` + +The first has explictly provided {null} to the argument "arg", while the second +has implicitly not provided a value to the argument "arg". These two forms may +be interpreted differently. For example, a mutation representing deleting a +field vs not altering a field, respectively. Niether form may be used for an +input expecting a Non-Null type. + +Note: The same two methods of representing the lack of a value are possible via +variables by either providing the a variable value as {null} and not providing +a variable value at all. + + ### Enum Value EnumValue : Name but not `true`, `false` or `null` @@ -745,9 +777,6 @@ recommended that Enum values be "all caps". Enum values are only used in contexts where the precise enumeration type is known. Therefore it's not necessary to supply an enumeration type name in the literal. -An enum value cannot be "null" in order to avoid confusion. GraphQL -does not supply a value literal to represent the concept {null}. - ### List Value diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 37df99e9e..89f830713 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -115,6 +115,9 @@ and floating-point values, they are interpreted as an integer input value if they have an empty fractional part (ex. `1.0`) and otherwise as floating-point input value. +For all types below, with the exception of Non-Null, if the explicit value +{null} is provided, then then the result of input coercion is {null}. + **Built-in Scalars** GraphQL provides a basic set of well-defined Scalar types. A GraphQL server @@ -809,16 +812,21 @@ input ExampleInputObject { Original Value | Variables | Coerced Value ------------------------------------------------------------------------------- -`{ a: "abc", b: 123 }` | `{}` | `{ a: "abc", b: 123 }` -`{ a: 123, b: "123" }` | `{}` | `{ a: "123", b: 123 }` -`{ a: "abc" }` | `{}` | Error: Missing required field {b} +`{ a: "abc", b: 123 }` | | `{ a: "abc", b: 123 }` +`{ a: 123, b: "123" }` | | `{ a: "123", b: 123 }` +`{ a: "abc" }` | | Error: Missing required field {b} +`{ a: "abc", b: null }` | | Error: {b} must be non-null. +`{ a: null, b: 1 }` | | `{ a: null, b: 1 }` `{ b: $var }` | `{ var: 123 }` | `{ b: 123 }` +`{ b: $var }` | `{}` | Error: Missing required field {b}. `{ b: $var }` | `{ var: null }` | Error: {b} must be non-null. -`{ b: $var }` | `{}` | Error: {b} must be non-null. -`{ b: $var }` | `{}` | Error: {b} must be non-null. `{ a: $var, b: 1 }` | `{ var: null }` | `{ a: null, b: 1 }` `{ a: $var, b: 1 }` | `{}` | `{ b: 1 }` +Note: there is a semantic difference between the input value +explicitly declaring an input field's value as the value {null} vs having not +declared the input field at all. + #### Input Object type validation 1. An Input Object type must define one or more fields. @@ -867,10 +875,21 @@ By default, all types in GraphQL are nullable; the {null} value is a valid response for all of the above types. To declare a type that disallows null, the GraphQL Non-Null type can be used. This type wraps an underlying type, and this type acts identically to that wrapped type, with the exception -that `null` is not a valid response for the wrapping type. A trailing +that {null} is not a valid response for the wrapping type. A trailing exclamation mark is used to denote a field that uses a Non-Null type like this: `name: String!`. +**Nullable vs. Optional** + +Fields are *always* optional within the context of a query, a field may be +omitted and the query is still valid. However fields that return Non-Null types +will never return the value {null} if queried. + +Inputs (such as field arguments), are always optional by default. However a +non-null input type is required. In addition to not accepting the value {null}, +it also does not accept omission. For the sake of simplicity nullable types +are always optional and non-null types are always required. + **Result Coercion** In all of the above result coercions, {null} was considered a valid value. @@ -881,43 +900,41 @@ must be raised. **Input Coercion** -If the argument of a Non-Null type is not provided, a query error must -be raised. +If an argument or input-object field of a Non-Null type is not provided, is +provided with the literal value {null}, or is provided with a variable that was +either not provided a value at runtime, or was provided the value {null}, then +a query error must be raised. -If an argument of a Non-Null type is provided with a literal value, it is -coerced using the input coercion for the wrapped type. +If the value provided to the Non-Null type is provided with a literal value +other than {null}, or a Non-Null variable value, it is coerced using the input coercion for the wrapped type. -If the argument of a Non-Null is provided with a variable, a query error must be -raised if the runtime provided value is not provided or is {null} in the -provided representation (usually JSON). Otherwise, the coerced value is the -result of using the input coercion for the wrapped type. - -Note that `null` is not a value in GraphQL, so a query cannot look like: +Example: A non-null argument cannot be omitted. ```!graphql { - field(arg: null) + fieldWithNonNullArg } ``` -to indicate that the argument is {null}. Instead, an argument would be {null} -only if it is omitted: +Example: The value {null} cannot be provided to a non-null argument. -```graphql +```!graphql { - field + fieldWithNonNullArg(nonNullArg: null) } ``` -Or if passed a variable of a nullable type that at runtime was not provided -a value: +Example: A variable of a nullable type cannot be provided to a non-null argument. ```graphql query withNullableVariable($var: String) { - field(arg: $var) + fieldWithNonNullArg(nonNullArg: $var) } ``` +Note: The Validation section defines providing a nullable variable type to +a non-null input type as invalid. + **Non-Null type validation** 1. A Non-Null type must not wrap another Non-Null type. diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index f95fcdaa6..18d33605d 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -629,22 +629,26 @@ fragment stringIntoInt on Arguments { ``` -#### Required Arguments +#### Required Non-Null Arguments * For each Field or Directive in the document. * Let {arguments} be the arguments provided by the Field or Directive. * Let {argumentDefinitions} be the set of argument definitions of that Field or Directive. - * For each {definition} in {argumentDefinitions} - * Let {type} be the expected type of {definition} - * If {type} is Non-Null - * Let {argumentName} be the name of {definition} + * For each {definition} in {argumentDefinitions}: + * Let {type} be the expected type of {definition}. + * If {type} is Non-Null: + * Let {argumentName} be the name of {definition}. * Let {argument} be the argument in {arguments} named {argumentName} * {argument} must exist. + * Let {value} be the value of {argument}. + * {value} must not be the {null} literal. ** Explanatory Text ** Arguments can be required. Arguments are required if the type of the argument -is non-null. If it is not non-null, the argument is optional. +is non-null. If it is not non-null, the argument is optional. When an argument +type is non-null, and is required, the explicit value {null} may also not +be provided. For example the following are valid: @@ -676,6 +680,13 @@ fragment missingRequiredArg on Arguments { } ``` +Providing the explicit value {null} is also not valid. + +```!graphql +fragment missingRequiredArg on Arguments { + notNullBooleanArgField(nonNullBooleanArg: null) +} +``` ## Fragments diff --git a/spec/Section 6 -- Execution.md b/spec/Section 6 -- Execution.md index 1c214629a..7d0172ad0 100644 --- a/spec/Section 6 -- Execution.md +++ b/spec/Section 6 -- Execution.md @@ -535,7 +535,7 @@ the field returned {null}, and an error must be added to the {"errors"} list in the response. If the result of resolving a field is {null} (either because the function to -resolve the field returned `null` or because an error occurred), and that +resolve the field returned {null} or because an error occurred), and that field is of a `Non-Null` type, then a field error is thrown. The error must be added to the {"errors"} list in the response.