diff --git a/spec/Appendix B -- Grammar Summary.md b/spec/Appendix B -- Grammar Summary.md index e75a0f5d4..e0001084f 100644 --- a/spec/Appendix B -- Grammar Summary.md +++ b/spec/Appendix B -- Grammar Summary.md @@ -47,7 +47,7 @@ Punctuator :: one of ! $ & ( ) ... : = @ [ ] { | } Name :: -- NameStart NameContinue\* [lookahead != NameContinue] +- NameStart NameContinue\* [lookahead != {NameContinue, `.`}] NameStart :: @@ -71,6 +71,11 @@ Digit :: one of - `0` `1` `2` `3` `4` `5` `6` `7` `8` `9` +QualifiedName :: + +- QualifiedName . Name [lookahead != `.`] +- Name . Name [lookahead != `.`] + IntValue :: IntegerPart [lookahead != {Digit, `.`, NameStart}] IntegerPart :: @@ -248,6 +253,7 @@ TypeSystemDefinition : - SchemaDefinition - TypeDefinition - DirectiveDefinition +- ServiceDefinition TypeSystemExtensionDocument : TypeSystemDefinitionOrExtension+ @@ -413,3 +419,19 @@ TypeSystemDirectiveLocation : one of - `ENUM_VALUE` - `INPUT_OBJECT` - `INPUT_FIELD_DEFINITION` + +ServiceDefinition : + +- Description service [lookahead != `{`] +- Description? service { ServiceAttribute+ } + +ServiceAttribute : + +- ServiceCapabilities + +ServiceCapabilities: capabilities { ServiceCapability+ } + +ServiceCapability: + +- Description? QualifiedName [lookahead != `(`] +- Description? QualifiedName ( StringValue ) diff --git a/spec/Section 2 -- Language.md b/spec/Section 2 -- Language.md index ab5a2542d..71b6008b3 100644 --- a/spec/Section 2 -- Language.md +++ b/spec/Section 2 -- Language.md @@ -186,7 +186,7 @@ lacks the punctuation often used to describe mathematical expressions. Name :: -- NameStart NameContinue\* [lookahead != NameContinue] +- NameStart NameContinue\* [lookahead != {NameContinue, `.`}] NameStart :: @@ -233,6 +233,18 @@ Any {Name} within a GraphQL type system must not start with two underscores {"\_\_"} unless it is part of the [introspection system](#sec-Introspection) as defined by this specification. +### Qualified Names + +QualifiedName :: + +- QualifiedName . Name [lookahead != `.`] +- Name . Name [lookahead != `.`] + +A qualified name is a case-sensitive string composed of two or more names +separated by a period (`.`). A qualified name allows for a structured chain of +names which can be useful for scoping or applying namespaces. A _capability +identifier_ is an example of a {QualifiedName}. + ## Descriptions Description : StringValue diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index 8400233fa..2775ea59d 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -12,6 +12,7 @@ TypeSystemDefinition : - SchemaDefinition - TypeDefinition - DirectiveDefinition +- ServiceDefinition The GraphQL language includes an [IDL](https://en.wikipedia.org/wiki/Interface_description_language) used to @@ -2227,3 +2228,96 @@ to the relevant IETF specification. ```graphql example scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122") ``` + +## Service Definition + +ServiceDefinition : + +- Description service [lookahead != `{`] +- Description? service { ServiceAttribute+ } + +ServiceAttribute : + +- ServiceCapabilities + +A GraphQL service is defined in terms of the capabilities that it offers which +are external to the schema. + +All capabilities within a service must have unique identifiers. + +### Service Capabilities + +ServiceCapabilities: capabilities { ServiceCapability+ } + +ServiceCapability: + +- Description? QualifiedName [lookahead != `(`] +- Description? QualifiedName ( StringValue ) + +:: A _service capability_ describes a feature supported by the GraphQL service +but not directly expressible via the type system. This may include support for +new or experimental GraphQL syntactic or behavioral features, protocol support +(such as GraphQL over WebSockets or Server-Sent Events), or additional +operational information (such as endpoints for related services). Service +capabilities may be supplied by the GraphQL implementation, the service, or +both. + +A _service capability_ is identified by a _capability identifier_ (a +{QualifiedName}), and may optionally have a string value. + +**Capability Identifier** + +:: A _capability identifier_ is a {QualifiedName} (a case-sensitive string value +composed of two or more {Name} separated by a period (`.`)) that uniquely +identifies a capability. This structure is inspired by reverse domain notation +to encourage global uniqueness and collision-resistance; it is recommended that +identifiers defined by specific projects, vendors, or implementations begin with +a prefix derived from a DNS name they control (e.g., {"com.example."}). + +Clients must compare capability identifiers using exact (case-sensitive) string +equality. + +**Reserved Capability Identifiers** + +A _capability identifier_ must not start with an underscore {"\_"}; this is +reserved for future usage. + +Capability identifiers beginning with the prefix {"graphql."} are reserved and +must not be used outside of official GraphQL Foundation specifications. +Identifiers beginning with the prefix {"graphql.rfc."} are reserved for RFC +proposals. + +Any identifiers beginning with case-insensitive variants of {"graphql."}, +{"org.graphql."} and {"gql."} are also reserved. + +Implementers should not change the meaning of capability identifiers; instead, a +new capability identifier should be used when the meaning changes. Implementers +should ensure that capability identifiers remain stable and version-agnostic +where possible. + +Note: Capability versioning, if needed, can be indicated using dot suffixes +(e.g.{ "org.example.capability.v2"}). + +This system enables incremental feature adoption and richer tooling +interoperability, while avoiding tight coupling to specific implementations. + +**Capability value** + +For capabilities that require more information than a simple indication of +support, a string value may be specified. + +For example, the capability {"graphql.onError"} does not require additional +information and thus does not specify a value; whereas +{"graphql.defaultErrorBehavior"} uses the value to indicate which _error +behavior_ is the default. + +**Specified capabilities** + +This version of the specification defines the following capabilities: + +- {"graphql.defaultErrorBehavior"} - indicates the _default error behavior_ of + the service via the {value}. If not present, assume the _default error + behavior_ is {"PROPAGATE"}. +- {"graphql.onError"} - indicates that the service allows the client to specify + {onError} in a request to indicate the _error behavior_ the service should use + for the request. No {value} is provided. diff --git a/spec/Section 4 -- Introspection.md b/spec/Section 4 -- Introspection.md index d7f8e629f..51a67916f 100644 --- a/spec/Section 4 -- Introspection.md +++ b/spec/Section 4 -- Introspection.md @@ -85,13 +85,14 @@ operation. ## Schema Introspection -The schema introspection system is accessible from the meta-fields `__schema` -and `__type` which are accessible from the type of the root of a query -operation. +The schema introspection system is accessible from the meta-fields `__schema`, +`__type` and `__service` which are accessible from the type of the root of a +query operation. ```graphql __schema: __Schema! __type(name: String!): __Type +__service: __Service! ``` Like all meta-fields, these are implicit and do not appear in the fields list in @@ -218,6 +219,16 @@ enum __DirectiveLocation { INPUT_OBJECT INPUT_FIELD_DEFINITION } + +type __Service { + capabilities: [__Capability!]! +} + +type __Capability { + identifier: String! + description: String + value: String +} ``` ### The \_\_Schema Type @@ -500,3 +511,29 @@ Fields\: {true}, deprecated arguments are also returned. - `isRepeatable` must return a Boolean that indicates if the directive may be used repeatedly at a single location. + +### The \_\_Service Type + +The `__Service` type is returned from the `__service` meta-field and provides +information about the GraphQL service, most notably about its capabilities. + +Note: Services implementing an older version of this specification may not +support the `__service` meta-field or `__Service` type. Support may be probed +using the introspection query: `{ __type(name: "__Service") { name } }`, a +{null} result indicates lack of support. + +Fields\: + +- `capabilities` must return a list of `__Capability` detailing each _service + capability_ supported by the service. + +### The \_\_Capability Type + +A `__Capability` object describes a specific _service capability_, and has the +following fields\: + +- `identifier` must return the string _capability identifier_ uniquely + identifying this service capability. +- `description` may return a String or {null}. +- `value` the String value of the service capability, or {null} if there is no + associated value. diff --git a/spec/Section 6 -- Execution.md b/spec/Section 6 -- Execution.md index d0de0ed2d..b9ef83b78 100644 --- a/spec/Section 6 -- Execution.md +++ b/spec/Section 6 -- Execution.md @@ -15,6 +15,10 @@ A GraphQL service generates a response from a request via execution. being executed. Conceptually, an initial value represents the "universe" of data available via a GraphQL Service. It is common for a GraphQL Service to always use the same initial value for every request. +- {onError} (optional): The _error behavior_ to apply to the request; see + [Handling Execution Errors](#sec-Handling-Execution-Errors). If {onError} is + provided and its value is not one of {"NULL"}, {"PROPAGATE"}, or {"HALT"}, + then a _request error_ must be raised. - {extensions} (optional): A map reserved for implementation-specific additional information. @@ -22,6 +26,13 @@ Given this information, the result of {ExecuteRequest(schema, document, operationName, variableValues, initialValue)} produces the response, to be formatted according to the Response section below. +Note: Previous versions of this specification did not define the {onError} +request attribute. Clients can detect support for {onError} by checking for the +{"org.graphql.onError"} capability. If this capability is not present, or if +capabilities themselves are not supported by introspection, then clients should +not include {onError} in the request and must assume the _error behavior_ is +{"PROPAGATE"}. + Implementations should not add additional properties to a _request_, which may conflict with future editions of the GraphQL specification. Instead, {extensions} provides a reserved location for implementation-specific additional @@ -600,13 +611,26 @@ section. If during {ExecuteCollectedFields()} a _response position_ with a non-null type -raises an _execution error_ then that error must propagate to the parent -response position (the entire selection set in the case of a field, or the -entire list in the case of a list position), either resolving to {null} if -allowed or being further propagated to a parent response position. - -If this occurs, any sibling response positions which have not yet executed or -have not yet yielded a value may be cancelled to avoid unnecessary work. +raises an _execution error_, the error must be added to the {"errors"} list in +the _execution result_ and then handled according to the _error behavior_ of the +request: + +- {"NULL"}: The _response position_ must be set to {null}, even if such position + is indicated by the schema to be non-nullable. (The client is responsible for + interpreting this {null} in conjunction with the {"errors"} list to + distinguish error results from intentional {null} values.) +- {"PROPAGATE"}: The _execution error_ must propagate to the parent _response + position_ (the entire selection set in the case of a field, or the entire list + in the case of a list position). The parent position resolves to {null} if + allowed, or else the error is further propagated to a parent response + position. Any sibling response positions that have not yet executed or have + not yet yielded a value may be cancelled to avoid unnecessary work. +- {"HALT"}: The current {ExecuteRootSelectionSet()} must be aborted immediately + and must yield an execution result with an {"errors"} list consisting of this + _execution error_ only and the {"data"} entry set to {null}. Any _response + position_ that has not yet executed or has not yet yielded a value may be + cancelled to avoid unnecessary work. (Note: For a subscription operation the + underlying stream is not terminated.) Note: See [Handling Execution Errors](#sec-Handling-Execution-Errors) for more about this behavior. @@ -902,17 +926,53 @@ ResolveAbstractType(abstractType, objectValue): An _execution error_ is an error raised during field execution, value resolution -or coercion, at a specific _response position_. While these errors must be -reported in the response, they are "handled" by producing partial {"data"} in -the _response_. +or coercion, at a specific _response position_. These errors must be added to +the {"errors"} list in the _response_, and are "handled" according to the _error +behavior_ of the request. + +Note: An _execution error_ is distinct from a _request error_ which results in a +response with no {"data"}. + +If a _response position_ resolves to {null} because of an execution error which +has already been added to the {"errors"} list in the _execution result_, the +{"errors"} list must not be further affected. That is, only one error should be +added to the errors list per _response position_. + +:: The _error behavior_ of a request indicates how an _execution error_ is +handled. It may be specified using the optional {onError} attribute of the +_request_. If omitted, the _default error behavior_ of the service applies. +Valid values for _error behavior_ are {"NULL"}, {"PROPAGATE"} and {"HALT"}. + +:: The _default error behavior_ of a service is implementation-defined. For +compatibility with existing clients, services should default to {"PROPAGATE"} +which reflects prior behavior. The default error behavior is indicated via the {"org.graphql.defaultErrorBehavior"} +_service capability_. + +Note: {"HALT"} is not recommended as the _default error behavior_ because it +prevents generating partial responses which may still contain useful data. + +Regardless of error behavior, if a _response position_ with a non-null type +results in {null} due to the result of {ResolveFieldValue()} then an execution +error must be raised at that position as specified in {CompleteValue()}. -Note: This is distinct from a _request error_ which results in a _request error -result_ with no data. +The _error behavior_ of a request applies to every _execution error_ raised +during execution. The following sections describe the behavior of each valid +value: -If an execution error is raised while resolving a field (either directly or -nested inside any lists), it is handled as though the _response position_ at -which the error occurred resolved to {null}, and the error must be added to the -{"errors"} list in the _execution result_. +**{"NULL"}** + +With {"NULL"}, a `Non-Null` _response position_ will have the value {null} if +and only if an error occurred at that position. + +Note: Clients must inspect the {"errors"} list and use the {"path"} of each +error result to distinguish between intentional {null} values and those +resulting from an _execution error_. + +**{"PROPAGATE"}** + +With {"PROPAGATE"}, a `Non-Null` _response position_ must not contain {null} in +the _response_. If the result of resolving a _response position_ is {null} (either due to the result of {ResolveFieldValue()} or because an execution error was raised), and @@ -920,11 +980,6 @@ that position is of a `Non-Null` type, then an execution error is raised at that position. The error must be added to the {"errors"} list in the _execution result_. -If a _response position_ resolves to {null} because of an execution error which -has already been added to the {"errors"} list in the _execution result_, the -{"errors"} list must not be further affected. That is, only one error should be -added to the errors list per _response position_. - Since `Non-Null` response positions cannot be {null}, execution errors are propagated to be handled by the parent _response position_. If the parent response position may be {null} then it resolves to {null}, otherwise if it is a @@ -939,3 +994,13 @@ position_ must resolve to {null}. If the `List` type is also wrapped in a If every _response position_ from the root of the request to the source of the execution error has a `Non-Null` type, then the {"data"} entry in the _execution result_ should be {null}. + +**{"HALT"}** + +With {"HALT"}, {ExecuteRootSelectionSet()} must cease immediately that the first +_execution error_ is raised. That error must be added to the {"errors"} list, +and {"data"} must be {null}. + +Note: For subscription operations, processing of the current event is ceased, +but the subscription still remains in place and future events will be processed +as normal.