From be984cb10737c27dfff42844e48e2fbc7ee5254a Mon Sep 17 00:00:00 2001 From: Ben Kraft Date: Tue, 31 May 2022 17:43:38 -0700 Subject: [PATCH 01/17] Add validation rule that operation types exist Right now the spec says that, for example, if the schema does not define a mutation root type, then the schema does not support mutations. But there's no validation rule for it, which means that many parsers (including graphql-js) treat a mutation as valid against such a schema. (Indeed, many end up considering *any* mutation as valid, since they don't know what type to validate the root selection set against.) This commit adds a validation rule to make the schema text explicit. Slated for discussion at the June 2 working group meeting. See also graphql/graphql-js#3592. --- spec/Section 3 -- Type System.md | 10 +++++---- spec/Section 5 -- Validation.md | 36 ++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index b539b936e..8b9296ac0 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -148,12 +148,14 @@ 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 (see +[Operation Type Existence](#sec-Operation-Type-Existence)). If it is provided, +it must be an Object type. 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. +not provided, the service does not support subscriptions (see +[Operation Type Existence](#sec-Operation-Type-Existence))s. If it is provided, +it must be an Object type. The {`query`}, {`mutation`}, and {`subscription`} root types must all be different types if provided. diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index 4eda8e7b4..3b1e0341d 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -129,6 +129,42 @@ extend type Dog { ## Operations +### All Operation Definitions + +#### Operation Type Existence + +**Formal Specification** + +- For each operation definition {operation} in the document. +- Let {operationRootType} be the root type in {schema} corresponding to the + type of {operation}. +- {operationRootType} must exist. + +**Explanatory Text** + +Each operation must reference an operation type which has a valid root type +in the schema. + +For example given the following schema: + +```graphql example +type Query { + hello: String +} +``` + +The following operation is valid: + +```graphql example +query helloQuery { hello } +``` + +While the following operation is invalid: + +```graphql example +mutation goodbyeMutation { goodbye } +``` + ### Named Operation Definitions #### Operation Name Uniqueness From 6b668ce61177a43a9a484e9a64ab8adfb3e756a8 Mon Sep 17 00:00:00 2001 From: Ben Kraft Date: Wed, 1 Jun 2022 13:47:43 -0700 Subject: [PATCH 02/17] prettier --- spec/Section 5 -- Validation.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index 3b1e0341d..d81e95dd1 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -136,14 +136,14 @@ extend type Dog { **Formal Specification** - For each operation definition {operation} in the document. -- Let {operationRootType} be the root type in {schema} corresponding to the - type of {operation}. +- Let {operationRootType} be the root type in {schema} corresponding to the type + of {operation}. - {operationRootType} must exist. **Explanatory Text** -Each operation must reference an operation type which has a valid root type -in the schema. +Each operation must reference an operation type which has a valid root type in +the schema. For example given the following schema: @@ -156,13 +156,17 @@ type Query { The following operation is valid: ```graphql example -query helloQuery { hello } +query helloQuery { + hello +} ``` While the following operation is invalid: ```graphql example -mutation goodbyeMutation { goodbye } +mutation goodbyeMutation { + goodbye +} ``` ### Named Operation Definitions From 011a9a580e978337734e19c91de3b9e9d823b707 Mon Sep 17 00:00:00 2001 From: Ben Kraft Date: Mon, 13 Jun 2022 14:50:06 -0700 Subject: [PATCH 03/17] typofix Co-authored-by: Benjie Gillam --- spec/Section 3 -- Type System.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 8b9296ac0..ee24e70fa 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -154,7 +154,7 @@ it must be an Object type. Similarly, the {`subscription`} root operation type is also optional; if it is not provided, the service does not support subscriptions (see -[Operation Type Existence](#sec-Operation-Type-Existence))s. If it is provided, +[Operation Type Existence](#sec-Operation-Type-Existence)). If it is provided, it must be an Object type. The {`query`}, {`mutation`}, and {`subscription`} root types must all be From 04197ab0054fa6c8f7fa160bdfffc698dde4dfaf Mon Sep 17 00:00:00 2001 From: Ben Kraft Date: Mon, 13 Jun 2022 14:51:06 -0700 Subject: [PATCH 04/17] indentation Co-authored-by: Benjie Gillam --- spec/Section 5 -- Validation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index d81e95dd1..54f1f4b28 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -135,10 +135,10 @@ extend type Dog { **Formal Specification** -- For each operation definition {operation} in the document. -- Let {operationRootType} be the root type in {schema} corresponding to the type +- For each operation definition {operation} in the document: + - Let {operationRootType} be the root type in {schema} corresponding to the type of {operation}. -- {operationRootType} must exist. + - {operationRootType} must exist. **Explanatory Text** From b7bf1073b7eed28de3030469ded7b04bd4443500 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Mon, 27 May 2024 14:56:57 -0400 Subject: [PATCH 05/17] Add 5.2.4 Operation Type Configuration --- spec/Section 5 -- Validation.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index 473cf5457..7dffd091d 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -332,6 +332,37 @@ contain any number of operations, each of which may contain different root fields. When executed, a document containing multiple subscription operations must provide the operation name as described in {GetOperation()}. +### 5.2.4 Operation Type Configuration + +#### Formal Specification + +For each operation definition in the document: +- Let operationType be the type of the operation (query, mutation, or subscription). +- The corresponding type for operationType must be defined in the schema. + +#### Explanatory Text + +The schema must be properly configured to handle mutation and subscription operation +types. This ensures that when a mutation or subscription operation is defined in the +document, the schema includes the appropriate type to handle these operations. + +While query operations are required for all schemas, mutation and subscription operations +are optional. If the schema does not include the necessary type for a mutation or subscription +operation defined in the document, it will be considered invalid. + +For example, the following document is valid if the schema includes a Mutation type, but +invalid if it does not: + +```graphql +mutation { + likeStory(storyID: 12345) { + story { + likeCount + } + } +} +``` + ## Fields ### Field Selections From 94e2bdab93b4c582f80bcf93f6b13bf956470a54 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Mon, 27 May 2024 15:02:03 -0400 Subject: [PATCH 06/17] Update spec/Section 5 -- Validation.md --- spec/Section 5 -- Validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index 7dffd091d..144c5430b 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -332,7 +332,7 @@ contain any number of operations, each of which may contain different root fields. When executed, a document containing multiple subscription operations must provide the operation name as described in {GetOperation()}. -### 5.2.4 Operation Type Configuration +### Operation Type Configuration #### Formal Specification From ef77474343c137562d5578f36dd38ff7427fd9dd Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Mon, 27 May 2024 15:03:38 -0400 Subject: [PATCH 07/17] Update spec/Section 5 -- Validation.md --- spec/Section 5 -- Validation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index 144c5430b..8936eb31e 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -337,8 +337,8 @@ must provide the operation name as described in {GetOperation()}. #### Formal Specification For each operation definition in the document: -- Let operationType be the type of the operation (query, mutation, or subscription). -- The corresponding type for operationType must be defined in the schema. +- Let {operationType} be the type of the operation (query, mutation, or subscription). +- The corresponding type for {operationType} must be defined in the schema. #### Explanatory Text From 0f283f9e5ef36e738bc722433c523fd42c2921db Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Mon, 27 May 2024 15:04:50 -0400 Subject: [PATCH 08/17] Update spec/Section 5 -- Validation.md --- spec/Section 5 -- Validation.md | 1 + 1 file changed, 1 insertion(+) diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index 8936eb31e..207901dd9 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -337,6 +337,7 @@ must provide the operation name as described in {GetOperation()}. #### Formal Specification For each operation definition in the document: + - Let {operationType} be the type of the operation (query, mutation, or subscription). - The corresponding type for {operationType} must be defined in the schema. From 6f187fa90f14ce86f6d6bafd6e9fa95ba5dab069 Mon Sep 17 00:00:00 2001 From: Shane Krueger Date: Tue, 4 Jun 2024 10:02:17 -0400 Subject: [PATCH 09/17] Apply suggestions from code review Co-authored-by: Benjie --- spec/Section 5 -- Validation.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index 207901dd9..4f855db27 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -332,20 +332,20 @@ contain any number of operations, each of which may contain different root fields. When executed, a document containing multiple subscription operations must provide the operation name as described in {GetOperation()}. -### Operation Type Configuration +### Operation Type Exists #### Formal Specification For each operation definition in the document: -- Let {operationType} be the type of the operation (query, mutation, or subscription). -- The corresponding type for {operationType} must be defined in the schema. +- Let {operationType} be the type of the operation (`query`, `mutation`, or `subscription`). +- The corresponding _root operation type_ for {operationType} must be defined in the schema. #### Explanatory Text -The schema must be properly configured to handle mutation and subscription operation -types. This ensures that when a mutation or subscription operation is defined in the -document, the schema includes the appropriate type to handle these operations. +A schema defines the root operation types that it supports. Any document that +contains an operation of a type unsupported by the schema is invalid, since such +an operation cannot be executed. While query operations are required for all schemas, mutation and subscription operations are optional. If the schema does not include the necessary type for a mutation or subscription @@ -354,7 +354,7 @@ operation defined in the document, it will be considered invalid. For example, the following document is valid if the schema includes a Mutation type, but invalid if it does not: -```graphql +```graphql example mutation { likeStory(storyID: 12345) { story { From b9f1f3305e395bb522b85e412d0772476803f857 Mon Sep 17 00:00:00 2001 From: Shane32 Date: Fri, 4 Oct 2024 19:28:43 -0400 Subject: [PATCH 10/17] Reformat --- spec/Section 5 -- Validation.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index e53854040..c71a40931 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -338,8 +338,10 @@ must provide the operation name as described in {GetOperation()}. For each operation definition in the document: -- Let {operationType} be the type of the operation (`query`, `mutation`, or `subscription`). -- The corresponding _root operation type_ for {operationType} must be defined in the schema. +- Let {operationType} be the type of the operation (`query`, `mutation`, or + `subscription`). +- The corresponding _root operation type_ for {operationType} must be defined in + the schema. #### Explanatory Text @@ -347,12 +349,13 @@ A schema defines the root operation types that it supports. Any document that contains an operation of a type unsupported by the schema is invalid, since such an operation cannot be executed. -While query operations are required for all schemas, mutation and subscription operations -are optional. If the schema does not include the necessary type for a mutation or subscription -operation defined in the document, it will be considered invalid. +While query operations are required for all schemas, mutation and subscription +operations are optional. If the schema does not include the necessary type for a +mutation or subscription operation defined in the document, it will be +considered invalid. -For example, the following document is valid if the schema includes a Mutation type, but -invalid if it does not: +For example, the following document is valid if the schema includes a Mutation +type, but invalid if it does not: ```graphql example mutation { From d5677894b8262963d2a1377332767ca5c3e43331 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Thu, 17 Oct 2024 18:02:30 +0100 Subject: [PATCH 11/17] Undo changes to Section 3 --- spec/Section 3 -- Type System.md | 287 +++++++++++++++++++------------ 1 file changed, 181 insertions(+), 106 deletions(-) diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index ee24e70fa..4f9356f0c 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -122,7 +122,7 @@ RootOperationTypeDefinition : OperationType : NamedType A GraphQL service's collective type system capabilities are referred to as that service's "schema". A schema is defined in terms of the types and directives it -supports as well as the root operation types for each kind of operation: query, +supports as well as the _root operation type_ for each kind of operation: query, mutation, and subscription; this determines the place in the type system where those operations begin. @@ -141,26 +141,24 @@ introspection system. ### Root Operation Types -A schema defines the initial root operation type for each kind of operation it -supports: query, mutation, and subscription; this determines the place in the +:: A schema defines the initial _root operation type_ for each kind of operation +it supports: query, mutation, and subscription; this determines the place in the type system where those operations begin. -The {`query`} root operation type must be provided and must be an Object type. +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 (see -[Operation Type Existence](#sec-Operation-Type-Existence)). If it is provided, -it 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. -Similarly, the {`subscription`} root operation type is also optional; if it is -not provided, the service does not support subscriptions (see -[Operation Type Existence](#sec-Operation-Type-Existence)). If it is provided, -it must be an Object type. +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. The {`query`}, {`mutation`}, and {`subscription`} root types must all be different types if provided. -The fields on the {`query`} root operation type indicate what fields are +The fields on the {`query`} _root operation type_ indicate what fields are available at the top level of a GraphQL query operation. For example, this example operation: @@ -171,7 +169,8 @@ query { } ``` -is only valid when the {`query`} root operation type has a field named "myName": +is only valid when the {`query`} _root operation type_ has a field named +"myName": ```graphql example type Query { @@ -179,8 +178,8 @@ type Query { } ``` -Similarly, the following mutation is only valid if the {`mutation`} root -operation type has a field named "setName". +Similarly, the following mutation is only valid if the {`mutation`} _root +operation type_ has a field named "setName". ```graphql example mutation { @@ -193,8 +192,8 @@ mutation { When using the type system definition language, a document must include at most one {`schema`} definition. -In this example, a GraphQL schema is defined with both query and mutation root -operation types: +In this example, a GraphQL schema is defined with both a query and mutation +_root operation type_: ```graphql example schema { @@ -213,18 +212,22 @@ type MyMutationRootType { **Default Root Operation Type Names** -While any type can be the root operation type for a GraphQL operation, the type -system definition language can omit the schema definition when the {`query`}, -{`mutation`}, and {`subscription`} root types are named {"Query"}, {"Mutation"}, -and {"Subscription"} respectively. +:: The _default root type name_ for each {`query`}, {`mutation`}, and +{`subscription`} _root operation type_ are {"Query"}, {"Mutation"}, and +{"Subscription"} respectively. + +The type system definition language can omit the schema definition when each +_root operation type_ uses its respective _default root type name_ and no other +type uses any _default root type name_. Likewise, when representing a GraphQL schema using the type system definition -language, a schema definition should be omitted if it only uses the default root -operation type names. +language, a schema definition should be omitted if each _root operation type_ +uses its respective _default root type name_ and no other type uses any _default +root type name_. This example describes a valid complete GraphQL schema, despite not explicitly including a {`schema`} definition. The {"Query"} type is presumed to be the -{`query`} root operation type of the schema. +{`query`} _root operation type_ of the schema. ```graphql example type Query { @@ -232,6 +235,30 @@ type Query { } ``` +This example describes a valid GraphQL schema without a {`mutation`} _root +operation type_, even though it contains a type named {"Mutation"}. The schema +definition must be included, otherwise the {"Mutation"} type would be +incorrectly presumed to be the {`mutation`} _root operation type_ of the schema. + +```graphql example +schema { + query: Query +} + +type Query { + latestVirus: Virus +} + +type Virus { + name: String + mutations: [Mutation] +} + +type Mutation { + name: String +} +``` + ### Schema Extension SchemaExtension : @@ -302,8 +329,8 @@ A GraphQL schema may describe that a field represents a list of another type; the `List` type is provided for this reason, and wraps another type. Similarly, the `Non-Null` type wraps another type, and denotes that the -resulting value will never be {null} (and that a field error cannot result in a -{null} value). +resulting value will never be {null} (and that a _field error_ cannot result in +a {null} value). These two types are referred to as "wrapping types"; non-wrapping types are referred to as "named types". A wrapping type has an underlying named type, @@ -320,23 +347,23 @@ can only be used as input types. Object, Interface, and Union types can only be used as output types. Lists and Non-Null types may be used as input types or output types depending on how the wrapped type may be used. -IsInputType(type) : +IsInputType(type): - If {type} is a List type or Non-Null type: - Let {unwrappedType} be the unwrapped type of {type}. - - Return IsInputType({unwrappedType}) + - Return IsInputType({unwrappedType}). - If {type} is a Scalar, Enum, or Input Object type: - - Return {true} -- Return {false} + - Return {true}. +- Return {false}. -IsOutputType(type) : +IsOutputType(type): - If {type} is a List type or Non-Null type: - Let {unwrappedType} be the unwrapped type of {type}. - - Return IsOutputType({unwrappedType}) + - Return IsOutputType({unwrappedType}). - If {type} is a Scalar, Object, Interface, Union, or Enum type: - - Return {true} -- Return {false} + - Return {true}. +- Return {false}. ### Type Extensions @@ -391,7 +418,7 @@ which, while serialized as a string, conforms to [RFC 4122](https://tools.ietf.org/html/rfc4122). When querying a field of type `UUID`, you can then rely on the ability to parse the result with a RFC 4122 compliant parser. Another example of a potentially useful custom scalar is -`URL`, which serializes as a string, but is guaranteed by the server to be a +`URL`, which serializes as a string, but is guaranteed by the service to be a valid URL. :: When defining a custom scalar, GraphQL services should provide a _scalar @@ -407,12 +434,17 @@ conform to its described rules. ```graphql example scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122") scalar URL @specifiedBy(url: "https://tools.ietf.org/html/rfc3986") +scalar DateTime + @specifiedBy(url: "https://scalars.graphql.org/andimarek/date-time") ``` Custom *scalar specification URL*s should provide a single, stable format to avoid ambiguity. If the linked specification is in flux, the service should link to a fixed version rather than to a resource which might change. +Note: Some community-maintained custom scalar specifications are hosted at +[scalars.graphql.org](https://scalars.graphql.org/). + Custom *scalar specification URL*s should not be changed once defined. Doing so would likely disrupt tooling or could introduce breaking changes within the linked specification's contents. @@ -421,20 +453,22 @@ Built-in scalar types must not provide a _scalar specification URL_ as they are specified by this document. Note: Custom scalars should also summarize the specified format and provide -examples in their description. +examples in their description; see the GraphQL scalars +[implementation guide](https://scalars.graphql.org/implementation-guide) for +more guidance. **Result Coercion and Serialization** A GraphQL service, when preparing a field of a given scalar type, must uphold the contract the scalar type describes, either by coercing the value or -producing a [field error](#sec-Errors.Field-errors) if a value cannot be coerced -or if coercion may result in data loss. +producing a _field error_ if a value cannot be coerced or if coercion may result +in data loss. A GraphQL service may decide to allow coercing different internal types to the expected return type. For example when coercing a field of type {Int} a boolean {true} value may produce {1} or a string value {"123"} may be parsed as base-10 {123}. However if internal type coercion cannot be reasonably performed without -losing information, then it must raise a field error. +losing information, then it must raise a _field error_. Since this coercion behavior is not observable to clients of the GraphQL service, the precise rules of coercion are left to the implementation. The only @@ -452,8 +486,8 @@ information on the serialization of scalars in common JSON and other formats. If a GraphQL service expects a scalar type as input to an argument, coercion is observable and the rules must be well defined. If an input value does not match -a coercion rule, a [request error](#sec-Errors.Request-errors) must be raised -(input values are validated before execution begins). +a coercion rule, a _request error_ must be raised (input values are validated +before execution begins). GraphQL has different constant literals to represent integer and floating-point input values, and coercion rules may apply differently depending on which type @@ -479,14 +513,15 @@ Fields returning the type {Int} expect to encounter 32-bit integer internal values. GraphQL services may coerce non-integer internal values to integers when -reasonable without losing information, otherwise they must raise a field error. -Examples of this may include returning `1` for the floating-point number `1.0`, -or returning `123` for the string `"123"`. In scenarios where coercion may lose -data, raising a field error is more appropriate. For example, a floating-point -number `1.2` should raise a field error instead of being truncated to `1`. +reasonable without losing information, otherwise they must raise a _field +error_. Examples of this may include returning `1` for the floating-point number +`1.0`, or returning `123` for the string `"123"`. In scenarios where coercion +may lose data, raising a field error is more appropriate. For example, a +floating-point number `1.2` should raise a field error instead of being +truncated to `1`. If the integer internal value represents a value less than -231 or -greater than or equal to 231, a field error should be raised. +greater than or equal to 231, a _field error_ should be raised. **Input Coercion** @@ -494,7 +529,7 @@ When expected as an input type, only integer input values are accepted. All other input values, including strings with numeric content, must raise a request error indicating an incorrect type. If the integer input value represents a value less than -231 or greater than or equal to 231, a -request error should be raised. +_request error_ should be raised. Note: Numeric integer values larger than 32-bit should either use String or a custom-defined Scalar type, as not all platforms and transports support encoding @@ -513,22 +548,22 @@ Fields returning the type {Float} expect to encounter double-precision floating-point internal values. GraphQL services may coerce non-floating-point internal values to {Float} when -reasonable without losing information, otherwise they must raise a field error. -Examples of this may include returning `1.0` for the integer number `1`, or -`123.0` for the string `"123"`. +reasonable without losing information, otherwise they must raise a _field +error_. Examples of this may include returning `1.0` for the integer number `1`, +or `123.0` for the string `"123"`. Non-finite floating-point internal values ({NaN} and {Infinity}) cannot be -coerced to {Float} and must raise a field error. +coerced to {Float} and must raise a _field error_. **Input Coercion** When expected as an input type, both integer and float input values are accepted. Integer input values are coerced to Float by adding an empty fractional part, for example `1.0` for the integer input value `1`. All other -input values, including strings with numeric content, must raise a request error -indicating an incorrect type. If the input value otherwise represents a value -not representable by finite IEEE 754 (e.g. {NaN}, {Infinity}, or a value outside -the available precision), a request error must be raised. +input values, including strings with numeric content, must raise a _request +error_ indicating an incorrect type. If the input value otherwise represents a +value not representable by finite IEEE 754 (e.g. {NaN}, {Infinity}, or a value +outside the available precision), a _request error_ must be raised. ### String @@ -544,14 +579,14 @@ that representation must be used to serialize this type. Fields returning the type {String} expect to encounter Unicode string values. GraphQL services may coerce non-string raw values to {String} when reasonable -without losing information, otherwise they must raise a field error. Examples of -this may include returning the string `"true"` for a boolean true value, or the -string `"1"` for the integer `1`. +without losing information, otherwise they must raise a _field error_. Examples +of this may include returning the string `"true"` for a boolean true value, or +the string `"1"` for the integer `1`. **Input Coercion** When expected as an input type, only valid Unicode string input values are -accepted. All other input values must raise a request error indicating an +accepted. All other input values must raise a _request error_ indicating an incorrect type. ### Boolean @@ -565,20 +600,20 @@ representation of the integers `1` and `0`. Fields returning the type {Boolean} expect to encounter boolean internal values. GraphQL services may coerce non-boolean raw values to {Boolean} when reasonable -without losing information, otherwise they must raise a field error. Examples of -this may include returning `true` for non-zero numbers. +without losing information, otherwise they must raise a _field error_. Examples +of this may include returning `true` for non-zero numbers. **Input Coercion** When expected as an input type, only boolean input values are accepted. All -other input values must raise a request error indicating an incorrect type. +other input values must raise a _request error_ indicating an incorrect type. ### ID The ID scalar type represents a unique identifier, often used to refetch an object or as the key for a cache. The ID type is serialized in the same way as a {String}; however, it is not intended to be human-readable. While it is often -numeric, it should always serialize as a {String}. +numeric, it must always serialize as a {String}. **Result Coercion** @@ -588,15 +623,15 @@ large 128-bit random numbers, to base64 encoded values, or string values of a format like [GUID](https://en.wikipedia.org/wiki/Globally_unique_identifier). GraphQL services should coerce as appropriate given the ID formats they expect. -When coercion is not possible they must raise a field error. +When coercion is not possible they must raise a _field error_. **Input Coercion** When expected as an input type, any string (such as `"4"`) or integer (such as `4` or `-4`) input value should be coerced to ID as appropriate for the ID formats a given GraphQL service expects. Any other input value, including float -input values (such as `4.0`), must raise a request error indicating an incorrect -type. +input values (such as `4.0`), must raise a _request error_ indicating an +incorrect type. ### Scalar Extensions @@ -642,8 +677,8 @@ operations, Objects describe the intermediate levels. GraphQL Objects represent a list of named fields, each of which yield 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. +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 @@ -857,7 +892,7 @@ Produces the ordered result: **Result Coercion** Determining the result of coercing an object is the heart of the GraphQL -executor, so this is covered in that section of the spec. +executor, see [Value Completion](#sec-Value-Completion). **Input Coercion** @@ -879,8 +914,12 @@ of rules must be adhered to by every Object type in a GraphQL schema. 4. For each argument of the field: 1. The argument must not have a name which begins with the characters {"\_\_"} (two underscores). - 2. The argument must accept a type where {IsInputType(argumentType)} + 2. The argument must have a unique name within that field; no two + arguments may share the same name. + 3. The argument must accept a type where {IsInputType(argumentType)} returns {true}. + 4. If argument type is Non-Null and a default value is not defined: + 1. The `@deprecated` directive must not be applied to this argument. 3. An object type may declare that it implements one or more unique interfaces. 4. An object type must be a super-set of all interfaces it implements: 1. Let this object type be {objectType}. @@ -923,14 +962,17 @@ IsValidImplementationFieldType(fieldType, implementedFieldType): 2. Let {implementedItemType} be the unwrapped item type of {implementedFieldType}. 3. Return {IsValidImplementationFieldType(itemType, implementedItemType)}. -3. If {fieldType} is the same type as {implementedFieldType} then return {true}. -4. If {fieldType} is an Object type and {implementedFieldType} is a Union type - and {fieldType} is a possible type of {implementedFieldType} then return - {true}. -5. If {fieldType} is an Object or Interface type and {implementedFieldType} is - an Interface type and {fieldType} declares it implements - {implementedFieldType} then return {true}. -6. Otherwise return {false}. +3. Return {IsSubType(fieldType, implementedFieldType)}. + +IsSubType(possibleSubType, superType): + +1. If {possibleSubType} is the same type as {superType} then return {true}. +2. If {possibleSubType} is an Object type and {superType} is a Union type and + {possibleSubType} is a possible type of {superType} then return {true}. +3. If {possibleSubType} is an Object or Interface type and {superType} is an + Interface type and {possibleSubType} declares it implements {superType} then + return {true}. +4. Otherwise return {false}. ### Field Arguments @@ -984,7 +1026,7 @@ Object, Interface, or Union type). ### Field Deprecation Fields in an object may be marked as deprecated as deemed necessary by the -application. It is still legal to include these fields in a selection set (to +application. It is still legal to include these fields in a _selection set_ (to ensure existing clients are not broken by the change), but the fields should be appropriately treated in documentation and tooling. @@ -1100,7 +1142,7 @@ type Contact { } ``` -This allows us to write a selection set for a `Contact` that can select the +This allows us to write a _selection set_ for a `Contact` that can select the common fields. ```graphql example @@ -1224,7 +1266,9 @@ Interface types have the potential to be invalid if incorrectly defined. 4. For each argument of the field: 1. The argument must not have a name which begins with the characters {"\_\_"} (two underscores). - 2. The argument must accept a type where {IsInputType(argumentType)} + 2. The argument must have a unique name within that field; no two + arguments may share the same name. + 3. The argument must accept a type where {IsInputType(argumentType)} returns {true}. 3. An interface type may declare that it implements one or more unique interfaces, but may not implement itself. @@ -1448,7 +1492,7 @@ enum Direction { **Result Coercion** GraphQL services must return one of the defined set of possible values. If a -reasonable coercion is not possible they must raise a field error. +reasonable coercion is not possible they must raise a _field error_. **Input Coercion** @@ -1584,10 +1628,10 @@ type of an Object or Interface field. **Input Coercion** The value for an input object should be an input object literal or an unordered -map supplied by a variable, otherwise a request error must be raised. In either -case, the input object literal or unordered map must not contain any entries -with names not defined by a field of this input object type, otherwise a -response error must be raised. +map supplied by a variable, otherwise a _request error_ must be raised. In +either case, the input object literal or unordered map must not contain any +entries with names not defined by a field of this input object type, otherwise a +request error must be raised. The result of coercion is an unordered map with an entry for each field both defined by the input object type and for which a value exists. The resulting map @@ -1610,7 +1654,7 @@ is constructed with the following rules: - If a variable is provided for an input object field, the runtime value of that variable must be used. If the runtime value is {null} and the field type is - non-null, a field error must be raised. If no runtime value is provided, the + non-null, a _field error_ must be raised. If no runtime value is provided, the variable definition's default value should be used. If the variable definition does not provide a default value, the input object field definition's default value should be used. @@ -1654,6 +1698,8 @@ input ExampleInputObject { {"\_\_"} (two underscores). 3. The input field must accept a type where {IsInputType(inputFieldType)} returns {true}. + 4. If input field type is Non-Null and a default value is not defined: + 1. The `@deprecated` directive must not be applied to this input field. 3. If an Input Object references itself either directly or through referenced Input Objects, at least one of the fields in the chain of references must be either a nullable or a List type. @@ -1696,13 +1742,14 @@ brackets like this: `pets: [Pet]`. Nesting lists is allowed: `matrix: [[Int]]`. GraphQL services must return an ordered list as the result of a list type. Each item in the list must be the result of a result coercion of the item type. If a -reasonable coercion is not possible it must raise a field error. In particular, -if a non-list is returned, the coercion should fail, as this indicates a -mismatch in expectations between the type system and the implementation. +reasonable coercion is not possible it must raise a _field error_. In +particular, if a non-list is returned, the coercion should fail, as this +indicates a mismatch in expectations between the type system and the +implementation. If a list's item type is nullable, then errors occurring during preparation or coercion of an individual item in the list must result in a the value {null} at -that position in the list along with a field error added to the response. If a +that position in the list along with a _field error_ added to the response. If a list's item type is non-null, a field error occurring at an individual item in the list must result in a field error for the entire list. @@ -1748,9 +1795,10 @@ 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 selection set, a field may -be omitted and the selection set is still valid. However fields that return -Non-Null types will never return the value {null} if queried. +Fields are _always_ optional within the context of a _selection set_, a field +may be omitted and the selection set is still valid (so long as the selection +set does not become empty). 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}, @@ -1762,19 +1810,20 @@ always optional and non-null types are always required. In all of the above result coercions, {null} was considered a valid value. To coerce the result of a Non-Null type, the coercion of the wrapped type should be performed. If that result was not {null}, then the result of coercing the -Non-Null type is that result. If that result was {null}, then a field error must -be raised. +Non-Null type is that result. If that result was {null}, then a _field error_ +must be raised. -Note: When a field error is raised on a non-null value, the error propagates to -the parent field. For more information on this process, see "Errors and -Non-Nullability" within the Execution section. +Note: When a _field error_ is raised on a non-null value, the error propagates +to the parent field. For more information on this process, see +[Errors and Non-Null Fields](#sec-Executing-Selection-Sets.Errors-and-Non-Null-Fields) +within the Execution section. **Input Coercion** 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 -request error must be raised. +_request error_ must be raised. 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 @@ -1996,7 +2045,9 @@ repeatable directives. 4. For each argument of the directive: 1. The argument must not have a name which begins with the characters {"\_\_"} (two underscores). - 2. The argument must accept a type where {IsInputType(argumentType)} returns + 2. The argument must have a unique name within that directive; no two + arguments may share the same name. + 3. The argument must accept a type where {IsInputType(argumentType)} returns {true}. ### @skip @@ -2049,26 +2100,47 @@ condition is false. ```graphql directive @deprecated( reason: String = "No longer supported" -) on FIELD_DEFINITION | ENUM_VALUE +) on FIELD_DEFINITION | ARGUMENT_DEFINITION | INPUT_FIELD_DEFINITION | ENUM_VALUE ``` The `@deprecated` _built-in directive_ is used within the type system definition language to indicate deprecated portions of a GraphQL service's schema, such as -deprecated fields on a type or deprecated enum values. +deprecated fields on a type, arguments on a field, input fields on an input +type, or values of an enum type. Deprecations include a reason for why it is deprecated, which is formatted using Markdown syntax (as specified by [CommonMark](https://commonmark.org/)). In this example type definition, `oldField` is deprecated in favor of using -`newField`. +`newField` and `oldArg` is deprecated in favor of using `newArg`. ```graphql example type ExampleType { newField: String oldField: String @deprecated(reason: "Use `newField`.") + + anotherField( + newArg: String + oldArg: String @deprecated(reason: "Use `newArg`.") + ): String } ``` +The `@deprecated` directive must not appear on required (non-null without a +default) arguments or input object field definitions. + +```graphql counter-example +type ExampleType { + invalidField( + newArg: String + oldArg: String! @deprecated(reason: "Use `newArg`.") + ): String +} +``` + +To deprecate a required argument or input field, it must first be made optional +by either changing the type to nullable or adding a default value. + ### @specifiedBy ```graphql @@ -2081,6 +2153,9 @@ behavior of [custom scalar types](#sec-Scalars.Custom-Scalars). The URL should point to a human-readable specification of the data format, serialization, and coercion rules. It must not appear on built-in scalar types. +Note: Details on implementing a GraphQL scalar specification can be found in the +[scalars.graphql.org implementation guide](https://scalars.graphql.org/implementation-guide). + In this example, a custom scalar type for `UUID` is defined with a URL pointing to the relevant IETF specification. From 55d4fd4244130db1b874a4902a3c89b65b7b922c Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Thu, 17 Oct 2024 18:07:57 +0100 Subject: [PATCH 12/17] Editorial --- spec/Section 5 -- Validation.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index 0fe86a4c0..14ab2f0f2 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -142,8 +142,8 @@ extend type Dog { **Formal Specification** - For each operation definition {operation} in the document: - - Let {operationRootType} be the root type in {schema} corresponding to the type - of {operation}. + - Let {rootOperationType} be the _root operation type_ in {schema} + corresponding to the kind of {operation}. - {operationRootType} must exist. **Explanatory Text** From eab709fa633a72404822739ac289e06e8589da6f Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Thu, 17 Oct 2024 18:11:00 +0100 Subject: [PATCH 13/17] Move Shane's content --- spec/Section 5 -- Validation.md | 57 +++++++++++++-------------------- 1 file changed, 22 insertions(+), 35 deletions(-) diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index f3f174a7d..fbc26fd4c 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -148,6 +148,28 @@ extend type Dog { **Explanatory Text** +A schema defines the root operation types that it supports. Any document that +contains an operation of a type unsupported by the schema is invalid, since such +an operation cannot be executed. + +While query operations are required for all schemas, mutation and subscription +operations are optional. If the schema does not include the necessary type for a +mutation or subscription operation defined in the document, it will be +considered invalid. + +For example, the following document is valid if the schema includes a Mutation +type, but invalid if it does not: + +```graphql example +mutation { + likeStory(storyID: 12345) { + story { + likeCount + } + } +} +``` + Each operation must reference an operation type which has a valid root type in the schema. @@ -372,41 +394,6 @@ contain any number of operations, each of which may contain different root fields. When executed, a document containing multiple subscription operations must provide the operation name as described in {GetOperation()}. -### Operation Type Exists - -#### Formal Specification - -For each operation definition in the document: - -- Let {operationType} be the type of the operation (`query`, `mutation`, or - `subscription`). -- The corresponding _root operation type_ for {operationType} must be defined in - the schema. - -#### Explanatory Text - -A schema defines the root operation types that it supports. Any document that -contains an operation of a type unsupported by the schema is invalid, since such -an operation cannot be executed. - -While query operations are required for all schemas, mutation and subscription -operations are optional. If the schema does not include the necessary type for a -mutation or subscription operation defined in the document, it will be -considered invalid. - -For example, the following document is valid if the schema includes a Mutation -type, but invalid if it does not: - -```graphql example -mutation { - likeStory(storyID: 12345) { - story { - likeCount - } - } -} -``` - ## Fields ### Field Selections From 8c6f5df311013f95ce4ebe020bb8133b281ebc94 Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Thu, 17 Oct 2024 18:18:14 +0100 Subject: [PATCH 14/17] More editorial --- spec/Section 5 -- Validation.md | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index fbc26fd4c..75512fe67 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -148,30 +148,13 @@ extend type Dog { **Explanatory Text** -A schema defines the root operation types that it supports. Any document that -contains an operation of a type unsupported by the schema is invalid, since such -an operation cannot be executed. +A schema defines the root operation type for each kind of operation that it +supports. Every schema must support `query` operations, however support for +`mutation` and `subscription` operations is optional. -While query operations are required for all schemas, mutation and subscription -operations are optional. If the schema does not include the necessary type for a -mutation or subscription operation defined in the document, it will be -considered invalid. - -For example, the following document is valid if the schema includes a Mutation -type, but invalid if it does not: - -```graphql example -mutation { - likeStory(storyID: 12345) { - story { - likeCount - } - } -} -``` - -Each operation must reference an operation type which has a valid root type in -the schema. +If the schema does not include the necessary _root operation type_ for an +operation defined in the document, that operation is invalid since it cannot be +executed. For example given the following schema: From 7a8975acb488fa5e857f0b36e7e8ed35401605ae Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Thu, 17 Oct 2024 18:20:50 +0100 Subject: [PATCH 15/17] More editorial --- spec/Section 5 -- Validation.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index 75512fe67..00ba6b601 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -148,13 +148,13 @@ extend type Dog { **Explanatory Text** -A schema defines the root operation type for each kind of operation that it +A schema defines the _root operation type_ for each kind of operation that it supports. Every schema must support `query` operations, however support for `mutation` and `subscription` operations is optional. -If the schema does not include the necessary _root operation type_ for an -operation defined in the document, that operation is invalid since it cannot be -executed. +If the schema does not include the necessary _root operation type_ for the kind +of an operation defined in the document, that operation is invalid since it +cannot be executed. For example given the following schema: From 06f237f6393df2076b38b4043376e589b37c1971 Mon Sep 17 00:00:00 2001 From: Benjie Date: Thu, 17 Oct 2024 18:27:25 +0100 Subject: [PATCH 16/17] Update spec/Section 5 -- Validation.md Co-authored-by: Shane Krueger --- spec/Section 5 -- Validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index 00ba6b601..07d27831f 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -144,7 +144,7 @@ extend type Dog { - For each operation definition {operation} in the document: - Let {rootOperationType} be the _root operation type_ in {schema} corresponding to the kind of {operation}. - - {operationRootType} must exist. + - {rootOperationType} must exist. **Explanatory Text** From b209bd8bae70b5adb3c111cdaf6cf37e9e091a23 Mon Sep 17 00:00:00 2001 From: Benjie Date: Thu, 17 Oct 2024 18:28:40 +0100 Subject: [PATCH 17/17] Update spec/Section 5 -- Validation.md --- spec/Section 5 -- Validation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index 07d27831f..66802a982 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -174,7 +174,7 @@ query helloQuery { While the following operation is invalid: -```graphql example +```graphql counter-example mutation goodbyeMutation { goodbye }