diff --git a/cspell.yml b/cspell.yml index 1a7446cbe..93e331965 100644 --- a/cspell.yml +++ b/cspell.yml @@ -11,6 +11,8 @@ words: - parallelization - structs - subselection + - fieldless + - typeless # Fictional characters / examples - alderaan - hagrid diff --git a/spec/Appendix C -- Grammar Summary.md b/spec/Appendix C -- Grammar Summary.md index 17e253e46..e60788d69 100644 --- a/spec/Appendix C -- Grammar Summary.md +++ b/spec/Appendix C -- Grammar Summary.md @@ -314,7 +314,7 @@ ImplementsInterfaces : - ImplementsInterfaces & NamedType - implements `&`? NamedType -FieldsDefinition : { FieldDefinition+ } +FieldsDefinition : { FieldDefinition\* } FieldDefinition : Description? Name ArgumentsDefinition? : Type Directives[Const]? @@ -345,6 +345,7 @@ UnionMemberTypes : - UnionMemberTypes | NamedType - = `|`? NamedType +- = { } UnionTypeExtension : diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index a33f30f5d..35d74dc5e 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -145,12 +145,12 @@ type system where those operations begin. The {`query`} _root operation type_ must be provided and must be an Object type. The {`mutation`} _root operation type_ is optional; if it is not provided, the -service does not support mutations. If it is provided, it must be an Object -type. +service does not support mutations. If it is provided, it must be an Object type +and must define at least one field. Similarly, the {`subscription`} _root operation type_ is also optional; if it is not provided, the service does not support subscriptions. If it is provided, it -must be an Object type. +must be an Object type and must define at least one field. The {`query`}, {`mutation`}, and {`subscription`} root types must all be different types if provided. @@ -207,6 +207,22 @@ type MyMutationRootType { } ``` +A schema intended only to process mutations may use an empty query _root +operation type_: + +```graphql example +schema { + query: IntrospectionOnlyRootType + mutation: TheMutationRootType +} + +type IntrospectionOnlyRootType {} + +type TheMutationRootType { + increaseSomeNumber(by: Int): Int +} +``` + **Default Root Operation Type Names** :: The _default root type name_ for each {`query`}, {`mutation`}, and @@ -326,12 +342,12 @@ in the system, allowing the definition of arbitrary type hierarchies. GraphQL supports two abstract types: interfaces and unions. -An `Interface` defines a list of fields; `Object` types and other Interface -types which implement this Interface are guaranteed to implement those fields. +An `Interface` defines a set of fields; `Object` types and other Interface types +which implement this Interface are guaranteed to implement those fields. Whenever a field claims it will return an Interface type, it will return a valid implementing Object type during _operation execution_. -A `Union` defines a list of possible types; similar to interfaces, whenever the +A `Union` defines a set of possible types; similar to interfaces, whenever the type system claims a union will be returned, one of the possible types will be returned. @@ -684,7 +700,7 @@ ImplementsInterfaces : - ImplementsInterfaces & NamedType - implements `&`? NamedType -FieldsDefinition : { FieldDefinition+ } +FieldsDefinition : { FieldDefinition\* } FieldDefinition : Description? Name ArgumentsDefinition? : Type Directives[Const]? @@ -693,11 +709,11 @@ GraphQL operations are hierarchical and composed, describing a tree of information. While Scalar types describe the leaf values of these hierarchical operations, Objects describe the intermediate levels. -GraphQL Objects represent a list of named fields, each of which yields a value -of a specific type. Object values should be serialized as ordered maps, where -the selected field names (or aliases) are the keys and the result of evaluating -the field is the value, ordered by the order in which they appear in the -_selection set_. +GraphQL Objects represent a set of named fields, each of which yields a value of +a specific type. Object values should be serialized as ordered maps, where the +selected field names (or aliases) are the keys and the result of evaluating the +field is the value, ordered by the order in which they appear in the _selection +set_. All fields defined within an Object type must not have a name which begins with {"\_\_"} (two underscores), as this is used exclusively by GraphQL's @@ -717,6 +733,19 @@ Where `name` is a field that will yield a {String} value, and `age` is a field that will yield an {Int} value, and `picture` is a field that will yield a `Url` value. +Object types may be defined with an empty set of fields: + +```graphql example +type Empty {} +``` + +An object type with no set of fields is incomplete and must be completed before +a schema containing it can be considered valid: + +```graphql counter-example +type Fieldless # Incomplete +``` + A query of an object value must be made via a selection set. This selection of fields will yield an ordered map containing exactly the subset of the object queried, which should be represented in the order in which they were queried. @@ -922,7 +951,7 @@ Objects are never valid inputs. Object types have the potential to be invalid if incorrectly defined. This set of rules must be adhered to by every Object type in a GraphQL schema. -1. An Object type must define one or more fields. +1. An Object type must define a set of fields. The set may be empty. 2. For each field of an Object type: 1. The field must have a unique name within that Object type; no two fields may share the same name. @@ -1090,6 +1119,14 @@ In this example, a directive is added to a `User` type without adding fields: extend type User @addedDirective ``` +An incomplete object type may be made complete by extending it with a set of +fields, even if that set is empty. For example, this object type extension +completes the `Fieldless` counter-example above: + +```graphql example +extend type Fieldless {} +``` + **Type Validation** Object type extensions have the potential to be invalid if incorrectly defined. @@ -1115,7 +1152,7 @@ InterfaceTypeDefinition : - Description? interface Name ImplementsInterfaces? Directives[Const]? [lookahead != `{`] -GraphQL interfaces represent a list of named fields and their arguments. GraphQL +GraphQL interfaces represent a set of named fields and their arguments. GraphQL objects and interfaces can then implement these interfaces which requires the implementing type to define all fields defined by those interfaces. @@ -1151,6 +1188,19 @@ type Business implements NamedEntity & ValuedEntity { } ``` +Interface types may also be defined with an empty set of fields: + +```graphql example +interface EmptyInterface {} +``` + +An interface type with no set of fields is incomplete and must be completed +before a schema containing it can be considered valid: + +```graphql counter-example +interface FieldlessInterface # Incomplete +``` + Fields which yield an interface are useful when one of many Object types are expected, but some fields should be guaranteed. @@ -1277,7 +1327,7 @@ Interfaces are never valid inputs. Interface types have the potential to be invalid if incorrectly defined. -1. An Interface type must define one or more fields. +1. An Interface type must define a set of fields. The set may be empty. 2. For each field of an Interface type: 1. The field must have a unique name within that Interface type; no two fields may share the same name. @@ -1341,6 +1391,14 @@ fields: extend interface NamedEntity @addedDirective ``` +An incomplete interface type may be made complete by extending it with a set of +fields, even if that set is empty. For example, this interface type extension +completes the `FieldlessInterface` counter-example above: + +```graphql example +extend interface FieldlessInterface {} +``` + **Type Validation** Interface type extensions have the potential to be invalid if incorrectly @@ -1368,8 +1426,9 @@ UnionMemberTypes : - UnionMemberTypes | NamedType - = `|`? NamedType +- = { } -GraphQL Unions represent an object that could be one of a list of GraphQL Object +GraphQL Unions represent an object that could be one of a set of GraphQL Object types, but provides for no guaranteed fields between those types. They also differ from interfaces in that Object types declare what interfaces they implement, but are not aware of what unions contain them. @@ -1438,6 +1497,19 @@ union SearchResult = | Person ``` +A union type may also be defined for an empty set of member types: + +```graphql example +union EmptyUnion = {} +``` + +A union type with no set of member types is incomplete and must be completed +before a schema containing it can be considered valid: + +```graphql counter-example +union TypelessUnion +``` + **Result Coercion** The union type should have some way of determining which object a given result @@ -1452,7 +1524,7 @@ Unions are never valid inputs. Union types have the potential to be invalid if incorrectly defined. -1. A Union type must include one or more unique member types. +1. A Union type must include a set of unique member types. The set may be empty. 2. The member types of a Union type must all be Object base types; Scalar, Interface and Union types must not be member types of a Union. Similarly, wrapping types must not be member types of a Union. @@ -1469,6 +1541,14 @@ from some previous union type. For example, this might be used to represent additional local data, or by a GraphQL service which is itself an extension of another GraphQL service. +An incomplete union type may be made complete by extending it with a set of +member types, even if that set is empty. For example, this union type extension +completes the `TypelessUnion` counter-example above: + +```graphql example +extend union TypelessUnion = {} +``` + **Type Validation** Union type extensions have the potential to be invalid if incorrectly defined.