Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Null value #83

Merged
merged 1 commit into from Oct 26, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions spec/Appendix B -- Grammar Summary.md
Expand Up @@ -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] :
Expand Down
35 changes: 32 additions & 3 deletions spec/Section 2 -- Language.md
Expand Up @@ -634,6 +634,7 @@ Value[Const] :
- FloatValue
- StringValue
- BooleanValue
- NullValue
- EnumValue
- ListValue[?Const]
- ObjectValue[?Const]
Expand Down Expand Up @@ -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`
Expand All @@ -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

Expand Down
65 changes: 41 additions & 24 deletions spec/Section 3 -- Type System.md
Expand Up @@ -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}.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"then then"

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the careful read, fixed in 3ce8b79


**Built-in Scalars**

GraphQL provides a basic set of well-defined Scalar types. A GraphQL server
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand All @@ -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.
Expand Down
23 changes: 17 additions & 6 deletions spec/Section 5 -- Validation.md
Expand Up @@ -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:

Expand Down Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion spec/Section 6 -- Execution.md
Expand Up @@ -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.

Expand Down