An alternative proposal for the often requested (#476, #542) ability to distinguish optional inputs from nullable inputs. It doesn't propose any change to core GraphQL, just conventions and directives.
It can be summarized as:
- Encourage using default values wherever possible, even a default of
null.
- Use standard directives as documentation. Extensions are free to implement validation based on the directives, but that's not part of the proposal.
Directives
"""
This input is optional, not nullable.
If the client insists on sending an explicit null value, the behavior is undefined.
"""
directive @optional on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION
"""
This input is nullable, not optional.
If the client insists on omitting the input value, the behavior is undefined.
"""
directive @required on ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION
Usage
| Nullability |
Optionality |
Defaults |
Definition |
| Non-Null |
Required |
|
Type! |
| Non-Null |
Optional |
Has Default |
Type! = value |
| Non-Null |
Optional |
No Default |
Type @optional |
| Nullable |
Required |
|
Type @required |
| Nullable |
Optional |
Omitted == null |
Type = null |
| Nullable |
Optional |
Omitted != null |
Type |
Descriptions
Type!
Works as always.
Type! = value
Already supported, just needs more documentation and encouragement. It may come as a surprise in this forum, but many developers don't know that an input can have a default, or that a default alone makes it optional.
- The introductory example uses a nullable:
length(unit: LengthUnit = METER): Float. Using LengthUnit! would make the example clearer.
- The spec contains conflicting statements which have become conventional wisdom:
For the sake of simplicity nullable types are always optional and non‐null types are always required.
Type @optional
The client knows that the service doesn't want an explicit null.
Type @required
The client knows that the service doesn't want an omitted input. Arguably this use case doesn't exist organically; it's for those who want to return an error on principle.
Type = null
Already supported; presumably it's not common because it appears pointless. But it has an important point in this context: guaranteeing to the client that omission and explicit null behave identically.
Type
Works as always, but with a different implication. Clients can infer that omission and explicit nulls have different semantics, otherwise one of the other options should have been chosen. A partial update mutation is the typical example, but this is common in queries as well. Consider filter predicates like (equal: String, contains: String, ...). null is likely a valid input to equal, and omission indicates to not filter. Whereas contains is likely being forced to be nullable, and could be annotated with @optional instead.
An alternative proposal for the often requested (#476, #542) ability to distinguish optional inputs from nullable inputs. It doesn't propose any change to core GraphQL, just conventions and directives.
It can be summarized as:
null.Directives
Usage
Type!Type! = valueType @optionalType @requiredType = nullTypeDescriptions
Type!Works as always.
Type! = valueAlready supported, just needs more documentation and encouragement. It may come as a surprise in this forum, but many developers don't know that an input can have a default, or that a default alone makes it optional.
length(unit: LengthUnit = METER): Float. UsingLengthUnit!would make the example clearer.Type @optionalThe client knows that the service doesn't want an explicit
null.Type @requiredThe client knows that the service doesn't want an omitted input. Arguably this use case doesn't exist organically; it's for those who want to return an error on principle.
Type = nullAlready supported; presumably it's not common because it appears pointless. But it has an important point in this context: guaranteeing to the client that omission and explicit null behave identically.
TypeWorks as always, but with a different implication. Clients can infer that omission and explicit nulls have different semantics, otherwise one of the other options should have been chosen. A partial update mutation is the typical example, but this is common in queries as well. Consider filter predicates like
(equal: String, contains: String, ...).nullis likely a valid input toequal, and omission indicates to not filter. Whereascontainsis likely being forced to be nullable, and could be annotated with@optionalinstead.