GraphQL
Current Working Draft
Introduction
This is the specification for GraphQL, a query language and execution engine originally created at Facebook in 2012 for describing the capabilities and requirements of data models for client-server applications. The development of this open standard started in 2015. This specification was licensed under OWFa 1.0 in 2017. The GraphQL Foundation was formed in 2019 as a neutral focal point for organizations who support the GraphQL ecosystem, and the GraphQL Specification Project was established also in 2019 as the Joint Development Foundation Projects, LLC, GraphQL Series.
If your organization benefits from GraphQL, please consider becoming a member and helping us to sustain the activities that support the health of our neutral ecosystem.
The GraphQL Specification Project has evolved and may continue to evolve in future editions of this specification. Previous editions of the GraphQL specification can be found at permalinks that match their release tag. The latest working draft release can be found at https://spec.graphql.org/draft.
Copyright Notice
Copyright © 2015-2018, Facebook, Inc.
Copyright © 2019-present, GraphQL contributors
THESE MATERIALS ARE PROVIDED “AS IS”. The parties expressly disclaim any warranties (express, implied, or otherwise), including implied warranties of merchantability, non-infringement, fitness for a particular purpose, or title, related to the materials. The entire risk as to implementing or otherwise using the materials is assumed by the implementer and user. IN NO EVENT WILL THE PARTIES BE LIABLE TO ANY OTHER PARTY FOR LOST PROFITS OR ANY FORM OF INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER FROM ANY CAUSES OF ACTION OF ANY KIND WITH RESPECT TO THIS DELIVERABLE OR ITS GOVERNING AGREEMENT, WHETHER BASED ON BREACH OF CONTRACT, TORT (INCLUDING NEGLIGENCE), OR OTHERWISE, AND WHETHER OR NOT THE OTHER MEMBER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Licensing
The GraphQL Specification Project is made available by the Joint Development Foundation. The current Working Group charter, which includes the IP policy governing all working group deliverables (including specifications, source code, and datasets) may be found at https://technical-charter.graphql.org.
Currently, the licenses governing GraphQL Specification Project deliverables are:
Deliverable | License |
---|---|
Specifications | Open Web Foundation Agreement 1.0 (Patent and Copyright Grants) |
Source code | MIT License |
Data sets | CC0 1.0 |
Conformance
A conforming implementation of GraphQL must fulfill all normative requirements. Conformance requirements are described in this document via both descriptive assertions and key words with clearly defined meanings.
The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative portions of this document are to be interpreted as described in IETF RFC 2119. These key words may appear in lowercase and still retain their meaning unless explicitly declared as non-normative.
A conforming implementation of GraphQL may provide additional functionality, but must not where explicitly disallowed or would otherwise result in non-conformance.
Conforming Algorithms
Algorithm steps phrased in imperative grammar (e.g. “Return the result of calling resolver”) are to be interpreted with the same level of requirement as the algorithm it is contained within. Any algorithm referenced within an algorithm step (e.g. “Let completedResult be the result of calling CompleteValue()”) is to be interpreted as having at least the same level of requirement as the algorithm containing that step.
Conformance requirements expressed as algorithms can be fulfilled by an implementation of this specification in any way as long as the perceived result is equivalent. Algorithms described in this document are written to be easy to understand. Implementers are encouraged to include equivalent but optimized implementations.
See Appendix A for more details about the definition of algorithms and other notational conventions used in this document.
Non-Normative Portions
All contents of this document are normative except portions explicitly declared as non-normative.
Examples in this document are non-normative, and are presented to aid understanding of introduced concepts and the behavior of normative portions of the specification. Examples are either introduced explicitly in prose (e.g. “for example”) or are set apart in example or counter-example blocks, like this:
Example № 1This is an example of a non-normative example.
Counter Example № 2This is an example of a non-normative counter-example.
Notes in this document are non-normative, and are presented to clarify intent, draw attention to potential edge-cases and pit-falls, and answer common questions that arise during implementation. Notes are either introduced explicitly in prose (e.g. “Note: “) or are set apart in a note block, like this:
1Overview
GraphQL is a query language designed to build client applications by providing an intuitive and flexible syntax and system for describing their data requirements and interactions.
For example, this GraphQL request will receive the name of the user with id 4 from the Facebook implementation of GraphQL.
Example № 3{
user(id: 4) {
name
}
}
Which produces the resulting data (in JSON):
Example № 4{
"user": {
"name": "Mark Zuckerberg"
}
}
GraphQL is not a programming language capable of arbitrary computation, but is instead a language used to make requests to application services that have capabilities defined in this specification. GraphQL does not mandate a particular programming language or storage system for application services that implement it. Instead, application services take their capabilities and map them to a uniform language, type system, and philosophy that GraphQL encodes. This provides a unified interface friendly to product development and a powerful platform for tool-building.
GraphQL has a number of design principles:
- Product-centric: GraphQL is unapologetically driven by the requirements of views and the front-end engineers that write them. GraphQL starts with their way of thinking and requirements and builds the language and runtime necessary to enable that.
- Hierarchical: Most product development today involves the creation and manipulation of view hierarchies. To achieve congruence with the structure of these applications, a GraphQL request itself is structured hierarchically. The request is shaped just like the data in its response. It is a natural way for clients to describe data requirements.
- Strong-typing: Every GraphQL service defines an application-specific type system. Requests are executed within the context of that type system. Given a GraphQL operation, tools can ensure that it is both syntactically correct and valid within that type system before execution, i.e. at development time, and the service can make certain guarantees about the shape and nature of the response.
- Client-specified response: Through its type system, a GraphQL service publishes the capabilities that its clients are allowed to consume. It is the client that is responsible for specifying exactly how it will consume those published capabilities. These requests are specified at field-level granularity. In the majority of client-server applications written without GraphQL, the service determines the shape of data returned from its various endpoints. A GraphQL response, on the other hand, contains exactly what a client asks for and no more.
- Introspective: GraphQL is introspective. A GraphQL service’s type system can be queryable by the GraphQL language itself, as will be described in this specification. GraphQL introspection serves as a powerful platform for building common tools and client software libraries.
Because of these principles, GraphQL is a powerful and productive environment for building client applications. Product developers and designers building applications against working GraphQL services—supported with quality tools—can quickly become productive without reading extensive documentation and with little or no formal training. To enable that experience, there must be those that build those services and tools.
The following formal specification serves as a reference for those builders. It describes the language and its grammar, the type system and the introspection system used to query it, and the execution and validation engines with the algorithms to power them. The goal of this specification is to provide a foundation and framework for an ecosystem of GraphQL tools, client libraries, and service implementations—spanning both organizations and platforms—that has yet to be built. We look forward to working with the community in order to do that.
2Language
Clients use the GraphQL query language to make requests to a GraphQL service. We refer to these request sources as documents. A document may contain operations (queries, mutations, and subscriptions) as well as fragments, a common unit of composition allowing for data requirement reuse.
A GraphQL document is defined as a syntactic grammar where terminal symbols are tokens (indivisible lexical units). These tokens are defined in a lexical grammar which matches patterns of source characters. In this document, syntactic grammar productions are distinguished with a colon :
while lexical grammar productions are distinguished with a double-colon ::
.
The source text of a GraphQL document must be a sequence of SourceCharacter. The character sequence must be described by a sequence of Token and Ignored lexical grammars. The lexical token sequence, omitting Ignored, must be described by a single Document syntactic grammar.
Lexical Analysis & Syntactic Parse
The source text of a GraphQL document is first converted into a sequence of lexical tokens, Token, and ignored tokens, Ignored. The source text is scanned from left to right, repeatedly taking the next possible sequence of code-points allowed by the lexical grammar productions as the next token. This sequence of lexical tokens are then scanned from left to right to produce an abstract syntax tree (AST) according to the Document syntactical grammar.
Lexical grammar productions in this document use lookahead restrictions to remove ambiguity and ensure a single valid lexical analysis. A lexical token is only valid if not followed by a character in its lookahead restriction.
For example, an IntValue has the restriction Digit, so cannot be followed by a Digit. Because of this, the sequence 123 cannot represent the tokens (12, 3) since 12 is followed by the Digit 3 and so must only represent a single token. Use WhiteSpace or other Ignored between characters to represent multiple tokens.
2.1Source Text
GraphQL documents are interpreted from a source text, which is a sequence of SourceCharacter, each SourceCharacter being a Unicode scalar value which may be any Unicode code point from U+0000 to U+D7FF or U+E000 to U+10FFFF (informally referred to as “characters” through most of this specification).
A GraphQL document may be expressed only in the ASCII range to be as widely compatible with as many existing tools, languages, and serialization formats as possible and avoid display issues in text editors and source control. Non-ASCII Unicode scalar values may appear within StringValue and Comment.
2.1.1White Space
White space is used to improve legibility of source text and act as separation between tokens, and any amount of white space may appear before or after any token. White space between tokens is not significant to the semantic meaning of a GraphQL Document, however white space characters may appear within a String or Comment token.
2.1.2Line Terminators
Like white space, line terminators are used to improve the legibility of source text and separate lexical tokens, any amount may appear before or after any other token and have no significance to the semantic meaning of a GraphQL Document.
2.1.3Comments
GraphQL source documents may contain single-line comments, starting with the # marker.
A comment may contain any SourceCharacter except LineTerminator so a comment always consists of all SourceCharacter starting with the # character up to but not including the LineTerminator (or end of the source).
Comments are Ignored like white space and may appear after any token, or before a LineTerminator, and have no significance to the semantic meaning of a GraphQL Document.
2.1.4Insignificant Commas
Similar to white space and line terminators, commas (,) are used to improve the legibility of source text and separate lexical tokens but are otherwise syntactically and semantically insignificant within GraphQL Documents.
Non-significant comma characters ensure that the absence or presence of a comma does not meaningfully alter the interpreted syntax of the document, as this can be a common user-error in other languages. It also allows for the stylistic use of either trailing commas or line terminators as list delimiters which are both often desired for legibility and maintainability of source code.
2.1.5Lexical Tokens
A GraphQL document is comprised of several kinds of indivisible lexical tokens defined here in a lexical grammar by patterns of source Unicode characters. Lexical tokens may be separated by Ignored tokens.
Tokens are later used as terminal symbols in GraphQL syntactic grammar rules.
2.1.6Ignored Tokens
Ignored tokens are used to improve readability and provide separation between lexical tokens, but are otherwise insignificant and not referenced in syntactical grammar productions.
Any amount of Ignored may appear before and after every lexical token. No ignored regions of a source document are significant, however SourceCharacter which appear in Ignored may also appear within a lexical Token in a significant way, for example a StringValue may contain white space characters. No Ignored may appear within a Token, for example no white space characters are permitted between the characters defining a FloatValue.
Byte Order Mark
The Byte Order Mark is a special Unicode code point which may appear at the beginning of a file which programs may use to determine the fact that the text stream is Unicode, and what specific encoding has been used. As files are often concatenated, a Byte Order Mark may appear before or after any lexical token and is Ignored.
2.1.7Punctuators
! | $ | & | ( | ) | ... | : | = | @ | [ | ] | { | | | } |
GraphQL documents include punctuation in order to describe structure. GraphQL is a data description language and not a programming language, therefore GraphQL lacks the punctuation often used to describe mathematical expressions.
2.1.8Names
A | B | C | D | E | F | G | H | I | J | K | L | M |
N | O | P | Q | R | S | T | U | V | W | X | Y | Z |
a | b | c | d | e | f | g | h | i | j | k | l | m |
n | o | p | q | r | s | t | u | v | w | x | y | z |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
GraphQL Documents are full of named things: operations, fields, arguments, types, directives, fragments, and variables. All names must follow the same grammatical form.
Names in GraphQL are case-sensitive. That is to say name
, Name
, and NAME
all refer to different names. Underscores are significant, which means other_name
and othername
are two different names.
A Name must not be followed by a NameContinue. In other words, a Name token is always the longest possible valid sequence. The source characters a1 cannot be interpreted as two tokens since a is followed by the NameContinue 1.
Reserved Names
Any Name within a GraphQL type system must not start with two underscores "__" unless it is part of the introspection system as defined by this specification.
2.2Document
A GraphQL Document describes a complete file or request string operated on by a GraphQL service or client. A document contains multiple definitions, either executable or representative of a GraphQL type system.
Documents are only executable by a GraphQL service if they are ExecutableDocument and contain at least one OperationDefinition. A Document which contains TypeSystemDefinitionOrExtension must not be executed; GraphQL execution services which receive a Document containing these should return a descriptive error.
GraphQL services which only seek to execute GraphQL requests and not construct a new GraphQL schema may choose to only permit ExecutableDocument.
Documents which do not contain OperationDefinition or do contain TypeSystemDefinitionOrExtension may still be parsed and validated to allow client tools to represent many GraphQL uses which may appear across many individual files.
If a Document contains only one operation, that operation may be unnamed. If that operation is a query without variables or directives then it may also be represented in the shorthand form, omitting both the query keyword as well as the operation name. Otherwise, if a GraphQL Document contains multiple operations, each operation must be named. When submitting a Document with multiple operations to a GraphQL service, the name of the desired operation to be executed must also be provided.
2.3Operations
query | mutation | subscription |
There are three types of operations that GraphQL models:
- query – a read-only fetch.
- mutation – a write followed by a fetch.
- subscription – a long-lived request that fetches data in response to source events.
Each operation is represented by an optional operation name and a selection set.
For example, this mutation operation might “like” a story and then retrieve the new number of likes:
Example № 5mutation {
likeStory(storyID: 12345) {
story {
likeCount
}
}
}
Query Shorthand
If a document contains only one operation and that operation is a query which defines no variables and has no directives applied to it then that operation may be represented in a short-hand form which omits the query keyword and operation name.
For example, this unnamed query operation is written via query shorthand.
Example № 6{
field
}
2.4Selection Sets
An operation selects the set of information it needs, and will receive exactly that information and nothing more, avoiding over-fetching and under-fetching data.
A selection set defines an ordered set of selections (fields, fragment spreads and inline fragments) against an object, union or interface type.
Example № 7{
id
firstName
lastName
}
In this query operation, the id
, firstName
, and lastName
fields form a selection set. Selection sets may also contain fragment references.
2.5Fields
A selection set is primarily composed of fields. A field describes one discrete piece of information available to request within a selection set.
Some fields describe complex data or relationships to other data. In order to further explore this data, a field may itself contain a selection set, allowing for deeply nested requests. All GraphQL operations must specify their selections down to fields which return scalar values to ensure an unambiguously shaped response.
For example, this operation selects fields of complex data and relationships down to scalar values.
Example № 8{
me {
id
firstName
lastName
birthday {
month
day
}
friends {
name
}
}
}
Fields in the top-level selection set of an operation often represent some information that is globally accessible to your application and its current viewer. Some typical examples of these top fields include references to a current logged-in viewer, or accessing certain types of data referenced by a unique identifier.
Example № 9# `me` could represent the currently logged in viewer.
{
me {
name
}
}
# `user` represents one of many users in a graph of data, referred to by a
# unique identifier.
{
user(id: 4) {
name
}
}
2.6Arguments
Fields are conceptually functions which return values, and occasionally accept arguments which alter their behavior. These arguments often map directly to function arguments within a GraphQL service’s implementation.
In this example, we want to query a specific user (requested via the id
argument) and their profile picture of a specific size
:
Example № 10{
user(id: 4) {
id
name
profilePic(size: 100)
}
}
Many arguments can exist for a given field:
Example № 11{
user(id: 4) {
id
name
profilePic(width: 100, height: 50)
}
}
Arguments Are Unordered
Arguments may be provided in any syntactic order and maintain identical semantic meaning.
These two operations are semantically identical:
Example № 12{
picture(width: 200, height: 100)
}
Example № 13{
picture(height: 100, width: 200)
}
2.7Field Alias
By default a field’s response key in the response object will use that field’s name. However, you can define a different response key by specifying an alias.
In this example, we can fetch two profile pictures of different sizes and ensure the resulting response object will not have duplicate keys:
Example № 14{
user(id: 4) {
id
name
smallPic: profilePic(size: 64)
bigPic: profilePic(size: 1024)
}
}
which returns the result:
Example № 15{
"user": {
"id": 4,
"name": "Mark Zuckerberg",
"smallPic": "https://cdn.site.io/pic-4-64.jpg",
"bigPic": "https://cdn.site.io/pic-4-1024.jpg"
}
}
The fields at the top level of an operation can also be given an alias:
Example № 16{
zuck: user(id: 4) {
id
name
}
}
which returns the result:
Example № 17{
"zuck": {
"id": 4,
"name": "Mark Zuckerberg"
}
}
2.8Fragments
Fragments are the primary unit of composition in GraphQL.
Fragments allow for the reuse of common repeated selections of fields, reducing duplicated text in the document. Inline Fragments can be used directly within a selection to condition upon a type condition when querying against an interface or union.
For example, if we wanted to fetch some common information about mutual friends as well as friends of some user:
Example № 18query noFragments {
user(id: 4) {
friends(first: 10) {
id
name
profilePic(size: 50)
}
mutualFriends(first: 10) {
id
name
profilePic(size: 50)
}
}
}
The repeated fields could be extracted into a fragment and composed by a parent fragment or operation.
Example № 19query withFragments {
user(id: 4) {
friends(first: 10) {
...friendFields
}
mutualFriends(first: 10) {
...friendFields
}
}
}
fragment friendFields on User {
id
name
profilePic(size: 50)
}
Fragments are consumed by using the spread operator (...
). All fields selected by the fragment will be added to the field selection at the same level as the fragment invocation. This happens through multiple levels of fragment spreads.
For example:
Example № 20query withNestedFragments {
user(id: 4) {
friends(first: 10) {
...friendFields
}
mutualFriends(first: 10) {
...friendFields
}
}
}
fragment friendFields on User {
id
name
...standardProfilePic
}
fragment standardProfilePic on User {
profilePic(size: 50)
}
The operations noFragments
, withFragments
, and withNestedFragments
all produce the same response object.
2.8.1Type Conditions
Fragments must specify the type they apply to. In this example, friendFields
can be used in the context of querying a User
.
Fragments cannot be specified on any input value (scalar, enumeration, or input object).
Fragments can be specified on object types, interfaces, and unions.
Selections within fragments only return values when the concrete type of the object it is operating on matches the type of the fragment.
For example in this operation using the Facebook data model:
Example № 21query FragmentTyping {
profiles(handles: ["zuck", "coca-cola"]) {
handle
...userFragment
...pageFragment
}
}
fragment userFragment on User {
friends {
count
}
}
fragment pageFragment on Page {
likers {
count
}
}
The profiles
root field returns a list where each element could be a Page
or a User
. When the object in the profiles
result is a User
, friends
will be present and likers
will not. Conversely when the result is a Page
, likers
will be present and friends
will not.
Example № 22{
"profiles": [
{
"handle": "zuck",
"friends": { "count": 1234 }
},
{
"handle": "coca-cola",
"likers": { "count": 90234512 }
}
]
}
2.8.2Inline Fragments
Fragments can also be defined inline within a selection set. This is useful for conditionally including fields based on a type condition or applying a directive to a selection set.
This feature of standard fragment inclusion was demonstrated in the query FragmentTyping
example above. We could accomplish the same thing using inline fragments.
Example № 23query inlineFragmentTyping {
profiles(handles: ["zuck", "coca-cola"]) {
handle
... on User {
friends {
count
}
}
... on Page {
likers {
count
}
}
}
}
Inline fragments may also be used to apply a directive to a group of fields. If the TypeCondition is omitted, an inline fragment is considered to be of the same type as the enclosing context.
Example № 24query inlineFragmentNoType($expandedInfo: Boolean) {
user(handle: "zuck") {
id
name
... @include(if: $expandedInfo) {
firstName
lastName
birthday
}
}
}
2.9Input Values
Field and directive arguments accept input values of various literal primitives; input values can be scalars, enumeration values, lists, or input objects.
If not defined as constant (for example, in DefaultValue), input values can be specified as a variable. List and inputs objects may also contain variables (unless defined to be constant).
2.9.1Int Value
An IntValue is specified without a decimal point or exponent but may be negative (ex. -123). It must not have any leading 0.
An IntValue must not be followed by a Digit. In other words, an IntValue token is always the longest possible valid sequence. The source characters 12 cannot be interpreted as two tokens since 1 is followed by the Digit 2. This also means the source 00 is invalid since it can neither be interpreted as a single token nor two 0 tokens.
An IntValue must not be followed by a . or NameStart. If either . or ExponentIndicator follows then the token must only be interpreted as a possible FloatValue. No other NameStart character can follow. For example the sequences 0x123
and 123L
have no valid lexical representations.
2.9.2Float Value
+ | - |
A FloatValue includes either a decimal point (ex. 1.0) or an exponent (ex. 1e50) or both (ex. 6.0221413e23) and may be negative. Like IntValue, it also must not have any leading 0.
A FloatValue must not be followed by a Digit. In other words, a FloatValue token is always the longest possible valid sequence. The source characters 1.23 cannot be interpreted as two tokens since 1.2 is followed by the Digit 3.
A FloatValue must not be followed by a .. For example, the sequence 1.23.4 cannot be interpreted as two tokens (1.2, 3.4).
A FloatValue must not be followed by a NameStart. For example the sequence 0x1.2p3
has no valid lexical representation.
2.9.3Boolean Value
true | false |
The two keywords true
and false
represent the two boolean values.
2.9.4String Value
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
A | B | C | D | E | F | ||||
a | b | c | d | e | f |
" | \ | / | b | f | n | r | t |
A StringValue is evaluated to a Unicode text value, a sequence of Unicode scalar value, by interpreting all escape sequences using the static semantics defined below. White space and other characters ignored between lexical tokens are significant within a string value.
The empty string "" must not be followed by another " otherwise it would be interpreted as the beginning of a block string. As an example, the source """""" can only be interpreted as a single empty block string and not three empty strings.
Escape Sequences
In a single-quoted StringValue, any Unicode scalar value may be expressed using an escape sequence. GraphQL strings allow both C-style escape sequences (for example \n
) and two forms of Unicode escape sequences: one with a fixed-width of 4 hexadecimal digits (for example \u000A
) and one with a variable-width most useful for representing a supplementary character such as an Emoji (for example \u{1F4A9}
).
The hexadecimal number encoded by a Unicode escape sequence must describe a Unicode scalar value, otherwise must result in a parse error. For example both sources "\uDEAD"
and "\u{110000}"
should not be considered valid StringValue.
Escape sequences are only meaningful within a single-quoted string. Within a block string, they are simply that sequence of characters (for example """\n"""
represents the Unicode text [U+005C, U+006E]). Within a comment an escape sequence is not a significant sequence of characters. They may not appear elsewhere in a GraphQL document.
Since StringCharacter must not contain some code points directly (for example, a LineTerminator), escape sequences must be used to represent them. All other escape sequences are optional and unescaped non-ASCII Unicode characters are allowed within strings. If using GraphQL within a system which only supports ASCII, then escape sequences may be used to represent all Unicode characters outside of the ASCII range.
For legacy reasons, a supplementary character may be escaped by two fixed-width unicode escape sequences forming a surrogate pair. For example the input "\uD83D\uDCA9"
is a valid StringValue which represents the same Unicode text as "\u{1F4A9}"
. While this legacy form is allowed, it should be avoided as a variable-width unicode escape sequence is a clearer way to encode such code points.
When producing a StringValue, implementations should use escape sequences to represent non-printable control characters (U+0000 to U+001F and U+007F to U+009F). Other escape sequences are not necessary, however an implementation may use escape sequences to represent any other range of code points (for example, when producing ASCII-only output). If an implementation chooses to escape a supplementary character, it should only use a variable-width unicode escape sequence.
Block Strings
Block strings are sequences of characters wrapped in triple-quotes ("""
). White space, line terminators, quote, and backslash characters may all be used unescaped to enable verbatim text. Characters must all be valid SourceCharacter.
Since block strings represent freeform text often used in indented positions, the string value semantics of a block string excludes uniform indentation and blank initial and trailing lines via BlockStringValue().
For example, the following operation containing a block string:
Example № 25mutation {
sendEmail(message: """
Hello,
World!
Yours,
GraphQL.
""")
}
Is identical to the standard quoted string:
Example № 26mutation {
sendEmail(message: "Hello,\n World!\n\nYours,\n GraphQL.")
}
Since block string values strip leading and trailing empty lines, there is no single canonical printed block string for a given value. Because block strings typically represent freeform text, it is considered easier to read if they begin and end with an empty line.
Example № 27"""
This starts with and ends with an empty line,
which makes it easier to read.
"""
Counter Example № 28"""This does not start with or end with any empty lines,
which makes it a little harder to read."""
Static Semantics
A StringValue describes a Unicode text value, which is a sequence of Unicode scalar value.
These semantics describe how to apply the StringValue grammar to a source text to evaluate a Unicode text. Errors encountered during this evaluation are considered a failure to apply the StringValue grammar to a source and must result in a parsing error.
- Return the Unicode text by concatenating the evaluation of all StringCharacter.
- Let value be the hexadecimal value represented by the sequence of HexDigit within EscapedUnicode.
- Assert value is a within the Unicode scalar value range (≥ 0x0000 and ≤ 0xD7FF or ≥ 0xE000 and ≤ 0x10FFFF).
- Return the Unicode scalar value value.
- Let leadingValue be the hexadecimal value represented by the first sequence of HexDigit.
- Let trailingValue be the hexadecimal value represented by the second sequence of HexDigit.
- If leadingValue is ≥ 0xD800 and ≤ 0xDBFF (a Leading Surrogate):
- Assert trailingValue is ≥ 0xDC00 and ≤ 0xDFFF (a Trailing Surrogate).
- Return (leadingValue - 0xD800) × 0x400 + (trailingValue - 0xDC00) + 0x10000.
- Otherwise:
- Assert leadingValue is within the Unicode scalar value range.
- Assert trailingValue is within the Unicode scalar value range.
- Return the sequence of the Unicode scalar value leadingValue followed by the Unicode scalar value trailingValue.
- Return the Unicode scalar value represented by EscapedCharacter according to the table below.
Escaped Character | Scalar Value | Character Name |
---|---|---|
" | U+0022 | double quote |
\ | U+005C | reverse solidus (back slash) |
/ | U+002F | solidus (forward slash) |
b | U+0008 | backspace |
f | U+000C | form feed |
n | U+000A | line feed (new line) |
r | U+000D | carriage return |
t | U+0009 | horizontal tab |
- Let rawValue be the Unicode text by concatenating the evaluation of all BlockStringCharacter (which may be an empty sequence).
- Return the result of BlockStringValue(rawValue).
- Let lines be the result of splitting rawValue by LineTerminator.
- Let commonIndent be null.
- For each line in lines:
- If line is the first item in lines, continue to the next line.
- Let length be the number of characters in line.
- Let indent be the number of leading consecutive WhiteSpace characters in line.
- If indent is less than length:
- If commonIndent is null or indent is less than commonIndent:
- Let commonIndent be indent.
- If commonIndent is null or indent is less than commonIndent:
- If commonIndent is not null:
- For each line in lines:
- If line is the first item in lines, continue to the next line.
- Remove commonIndent characters from the beginning of line.
- For each line in lines:
- While the first item line in lines contains only WhiteSpace:
- Remove the first item from lines.
- While the last item line in lines contains only WhiteSpace:
- Remove the last item from lines.
- Let formatted be the empty character sequence.
- For each line in lines:
- If line is the first item in lines:
- Append formatted with line.
- Otherwise:
- Append formatted with a line feed character (U+000A).
- Append formatted with line.
- If line is the first item in lines:
- Return formatted.
2.9.5Null Value
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:
Example № 29{
field(arg: null)
field
}
The first has explicitly 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. Neither form may be used for an input expecting a Non-Null type.
2.9.6Enum Value
Enum values are represented as unquoted names (ex. MOBILE_WEB
). It is 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.
2.9.7List Value
Lists are ordered sequences of values wrapped in square-brackets [ ]
. The values of a List literal may be any value literal or variable (ex. [1, 2, 3]
).
Commas are optional throughout GraphQL so trailing commas are allowed and repeated commas do not represent missing values.
Semantics
2.9.8Input Object Values
Input object literal values are unordered lists of keyed input values wrapped in curly-braces { }
. The values of an object literal may be any input value literal or variable (ex. { name: "Hello world", score: 1.0 }
). We refer to literal representation of input objects as “object literals.”
Input Object Fields Are Unordered
Input object fields may be provided in any syntactic order and maintain identical semantic meaning.
These two operations are semantically identical:
Example № 30{
nearestThing(location: { lon: 12.43, lat: -53.211 })
}
Example № 31{
nearestThing(location: { lat: -53.211, lon: 12.43 })
}
Semantics
- Let inputObject be a new input object value with no fields.
- For each field in ObjectFieldlist:
- Return inputObject.
2.10Variables
A GraphQL operation can be parameterized with variables, maximizing reuse, and avoiding costly string building in clients at runtime.
If not defined as constant (for example, in DefaultValue), a Variable can be supplied for an input value.
Variables must be defined at the top of an operation and are in scope throughout the execution of that operation. Values for those variables are provided to a GraphQL service as part of a request so they may be substituted in during execution.
In this example, we want to fetch a profile picture size based on the size of a particular device:
Example № 32query getZuckProfile($devicePicSize: Int) {
user(id: 4) {
id
name
profilePic(size: $devicePicSize)
}
}
If providing JSON for the variables’ values, we could request a profilePic
of size 60
:
Example № 33{
"devicePicSize": 60
}
Variable Use Within Fragments
Variables can be used within fragments. Variables have global scope with a given operation, so a variable used within a fragment must be declared in any top-level operation that transitively consumes that fragment. If a variable is referenced in a fragment and is included by an operation that does not define that variable, that operation is invalid (see All Variable Uses Defined).
2.11Type References
GraphQL describes the types of data expected by arguments and variables. Input types may be lists of another input type, or a non-null variant of any other input type.
Semantics
- Let name be the string value of Name.
- Let type be the type defined in the Schema named name.
- type must not be null.
- Return type.
2.12Directives
Directives provide a way to describe alternate runtime execution and type validation behavior in a GraphQL document.
In some cases, you need to provide options to alter GraphQL’s execution behavior in ways field arguments will not suffice, such as conditionally including or skipping a field. Directives provide this by describing additional information to the executor.
Directives have a name along with a list of arguments which may accept values of any input type.
Directives can be used to describe additional information for types, fields, fragments and operations.
As future versions of GraphQL adopt new configurable execution capabilities, they may be exposed via directives. GraphQL services and tools may also provide any additional custom directive beyond those described here.
Directive Order Is Significant
Directives may be provided in a specific syntactic order which may have semantic interpretation.
These two type definitions may have different semantic meaning:
Example № 34type Person
@addExternalFields(source: "profiles")
@excludeField(name: "photo") {
name: String
}
Example № 35type Person
@excludeField(name: "photo")
@addExternalFields(source: "profiles") {
name: String
}
3Type System
The GraphQL Type system describes the capabilities of a GraphQL service and is used to determine if a requested operation is valid, to guarantee the type of response results, and describes the input types of variables to determine if values provided at request time are valid.
The GraphQL language includes an IDL used to describe a GraphQL service’s type system. Tools may use this definition language to provide utilities such as client code generation or service boot-strapping.
GraphQL tools or services which only seek to execute GraphQL requests and not construct a new GraphQL schema may choose not to allow TypeSystemDefinition. Tools which only seek to produce schema and not execute requests may choose to only allow TypeSystemDocument and not allow ExecutableDefinition or TypeSystemExtension but should provide a descriptive error if present.
3.1Type System Extensions
Type system extensions are used to represent a GraphQL type system which has been extended from some original type system. For example, this might be used by a local service to represent data a GraphQL client only accesses locally, or by a GraphQL service which is itself an extension of another GraphQL service.
Tools which only seek to produce and extend schema and not execute requests may choose to only allow TypeSystemExtensionDocument and not allow ExecutableDefinition but should provide a descriptive error if present.
3.2Descriptions
Documentation is a first-class feature of GraphQL type systems. To ensure the documentation of a GraphQL service remains consistent with its capabilities, descriptions of GraphQL definitions are provided alongside their definitions and made available via introspection.
To allow GraphQL service designers to easily publish documentation alongside the capabilities of a GraphQL service, GraphQL descriptions are defined using the Markdown syntax (as specified by CommonMark). In the type system definition language, these description strings (often BlockString) occur immediately before the definition they describe.
GraphQL schema and all other definitions (e.g. types, fields, arguments, etc.) which can be described should provide a Description unless they are considered self descriptive.
As an example, this simple GraphQL schema is well described:
Example № 36"""
A simple GraphQL schema which is well described.
"""
schema {
query: Query
}
"""
Root type for all your query operations
"""
type Query {
"""
Translates a string from a given language into a different language.
"""
translate(
"The original language that `text` is provided in."
fromLanguage: Language
"The translated language to be returned."
toLanguage: Language
"The text to be translated."
text: String
): String
}
"""
The set of languages supported by `translate`.
"""
enum Language {
"English"
EN
"French"
FR
"Chinese"
CH
}
3.3Schema
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 type for each kind of operation: query, mutation, and subscription; this determines the place in the type system where those operations begin.
A GraphQL schema must itself be internally valid. This section describes the rules for this validation process where relevant.
All types within a GraphQL schema must have unique names. No two provided types may have the same name. No provided type may have a name which conflicts with any built in types (including Scalar and Introspection types).
All directives within a GraphQL schema must have unique names.
All types and directives defined within a schema must not have a name which begins with "__" (two underscores), as this is used exclusively by GraphQL’s introspection system.
3.3.1Root 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 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.
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 available at the top level of a GraphQL query operation.
For example, this example operation:
Example № 37query {
myName
}
is only valid when the query root operation type has a field named “myName”:
Example № 38type Query {
myName: String
}
Similarly, the following mutation is only valid if the mutation root operation type has a field named “setName”.
Example № 39mutation {
setName(name: "Zuck") {
newName
}
}
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 a query and mutation root operation type:
Example № 40schema {
query: MyQueryRootType
mutation: MyMutationRootType
}
type MyQueryRootType {
someField: String
}
type MyMutationRootType {
setSomeField(to: String): String
}
Default Root Operation Type Names
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 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.
Example № 41type Query {
someField: String
}
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.
Example № 42schema {
query: Query
}
type Query {
latestVirus: Virus
}
type Virus {
name: String
mutations: [Mutation]
}
type Mutation {
name: String
}
3.3.2Schema Extension
Schema extensions are used to represent a schema which has been extended from an original schema. For example, this might be used by a GraphQL service which adds additional operation types, or additional directives to an existing schema.
Schema Validation
Schema extensions have the potential to be invalid if incorrectly defined.
- The Schema must already be defined.
- Any non-repeatable directives provided must not already apply to the original Schema.
3.4Types
The fundamental unit of any GraphQL Schema is the type. There are six kinds of named type definitions in GraphQL, and two wrapping types.
The most basic type is a Scalar
. A scalar represents a primitive value, like a string or an integer. Oftentimes, the possible responses for a scalar field are enumerable. GraphQL offers an Enum
type in those cases, where the type specifies the space of valid responses.
Scalars and Enums form the leaves in response trees; the intermediate levels are Object
types, which define a set of fields, where each field is another type 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. Whenever a field claims it will return an Interface type, it will return a valid implementing Object type during execution.
A Union
defines a list of possible types; similar to interfaces, whenever the type system claims a union will be returned, one of the possible types will be returned.
Finally, oftentimes it is useful to provide complex structs as inputs to GraphQL field arguments or variables; the Input Object
type allows the schema to define exactly what data is expected.
3.4.1Wrapping Types
All of the types so far are assumed to be both nullable and singular: e.g. a scalar string returns either null or a singular string.
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).
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, found by continually unwrapping the type until a named type is found.
3.4.2Input and Output Types
Types are used throughout GraphQL to describe both the values accepted as input to arguments and variables as well as the values output by fields. These two uses categorize types as input types and output types. Some kinds of types, like Scalar and Enum types, can be used as both input types and output types; other kinds of types can only be used in one or the other. Input Object types 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.
- If type is a List type or Non-Null type:
- Let unwrappedType be the unwrapped type of type.
- Return IsInputType(unwrappedType).
- If type is a Scalar, Enum, or Input Object type:
- Return true.
- Return false.
- If type is a List type or Non-Null type:
- Let unwrappedType be the unwrapped type of type.
- Return IsOutputType(unwrappedType).
- If type is a Scalar, Object, Interface, Union, or Enum type:
- Return true.
- Return false.
3.4.3Type Extensions
Type extensions are used to represent a GraphQL type which has been extended from some original type. For example, this might be used by a local service to represent additional fields a GraphQL client only accesses locally.
3.5Scalars
Scalar types represent primitive leaf values in a GraphQL type system. GraphQL responses take the form of a hierarchical tree; the leaves of this tree are typically GraphQL Scalar types (but may also be Enum types or null values).
GraphQL provides a number of built-in scalars which are fully defined in the sections below, however type systems may also add additional custom scalars to introduce additional semantic meaning.
Built-in Scalars
GraphQL specifies a basic set of well-defined Scalar types: Int, Float, String, Boolean, and ID. A GraphQL framework should support all of these types, and a GraphQL service which provides a type by these names must adhere to the behavior described for them in this document. As an example, a service must not include a type called Int and use it to represent 64-bit numbers, internationalization information, or anything other than what is defined in this document.
When returning the set of types from the __Schema
introspection type, all referenced built-in scalars must be included. If a built-in scalar type is not referenced anywhere in a schema (there is no field, argument, or input field of that type) then it must not be included.
When representing a GraphQL schema using the type system definition language, all built-in scalars must be omitted for brevity.
Custom Scalars
GraphQL services may use custom scalar types in addition to the built-in scalars. For example, a GraphQL service could define a scalar called UUID
which, while serialized as a string, conforms to RFC 4122. 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 service to be a valid URL.
When defining a custom scalar, GraphQL services should provide a scalar specification URL via the @specifiedBy
directive or the specifiedByURL
introspection field. This URL must link to a human-readable specification of the data format, serialization, and coercion rules for the scalar.
For example, a GraphQL service providing a UUID
scalar may link to RFC 4122, or some custom document defining a reasonable subset of that RFC. If a scalar specification URL is present, systems and tools that are aware of it should conform to its described rules.
Example № 43scalar 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 URLs 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.
Custom scalar specification URLs should not be changed once defined. Doing so would likely disrupt tooling or could introduce breaking changes within the linked specification’s contents.
Built-in scalar types must not provide a scalar specification URL as they are specified by this document.
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 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.
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 requirement is that the service must yield values which adhere to the expected Scalar type.
GraphQL scalars are serialized according to the serialization format being used. There may be a most appropriate serialized primitive for each given scalar type, and the service should produce each primitive where appropriate.
See Serialization Format for more detailed information on the serialization of scalars in common JSON and other formats.
Input Coercion
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 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 of input value is encountered. GraphQL may be parameterized by variables, the values of which are often serialized when sent over a transport like HTTP. Since some common serializations (ex. JSON) do not discriminate between integer 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 the result of input coercion is null.
3.5.1Int
The Int scalar type represents a signed 32-bit numeric non-fractional value. Response formats that support a 32-bit integer or a number type should use that type to represent this scalar.
Result Coercion
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
.
If the integer internal value represents a value less than -231 or greater than or equal to 231, a field error should be raised.
Input Coercion
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.
3.5.2Float
The Float scalar type represents signed double-precision finite values as specified by IEEE 754. Response formats that support an appropriate double-precision number type should use that type to represent this scalar.
Result Coercion
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"
.
Non-finite floating-point internal values (NaN and Infinity) cannot be 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.
3.5.3String
The String scalar type represents textual data, represented as a sequence of Unicode code points. The String type is most often used by GraphQL to represent free-form human-readable text. How the String is encoded internally (for example UTF-8) is left to the service implementation. All response serialization formats must support a string representation (for example, JSON Unicode strings), and that representation must be used to serialize this type.
Result Coercion
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
.
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 incorrect type.
3.5.4Boolean
The Boolean scalar type represents true
or false
. Response formats should use a built-in boolean type if supported; otherwise, they should use their representation of the integers 1
and 0
.
Result Coercion
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.
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.
3.5.5ID
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 must always serialize as a String.
Result Coercion
GraphQL is agnostic to ID format, and serializes to string to ensure consistency across many formats ID could represent, from small auto-increment numbers, to large 128-bit random numbers, to base64 encoded values, or string values of a format like GUID.
GraphQL services should coerce as appropriate given the ID formats they expect. 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.
3.5.6Scalar Extensions
Scalar type extensions are used to represent a scalar type which has been extended from some original scalar type. For example, this might be used by a GraphQL tool or service which adds directives to an existing scalar.
Type Validation
Scalar type extensions have the potential to be invalid if incorrectly defined.
- The named type must already be defined and must be a Scalar type.
- Any non-repeatable directives provided must not already apply to the original Scalar type.
3.6Objects
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 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.
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 introspection system.
For example, a type Person
could be described as:
Example № 44type Person {
name: String
age: Int
picture: Url
}
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.
A query of an object value must select at least one field. 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. Only fields that are declared on the object type may validly be queried on that object.
For example, selecting all the fields of Person
:
Example № 45{
name
age
picture
}
Would yield the object:
Example № 46{
"name": "Mark Zuckerberg",
"age": 30,
"picture": "http://some.cdn/picture.jpg"
}
While selecting a subset of fields:
Example № 47{
age
name
}
Must only yield exactly that subset:
Example № 48{
"age": 30,
"name": "Mark Zuckerberg"
}
A field of an Object type may be a Scalar, Enum, another Object type, an Interface, or a Union. Additionally, it may be any wrapping type whose underlying base type is one of those five.
For example, the Person
type might include a relationship
:
Example № 49type Person {
name: String
age: Int
picture: Url
relationship: Person
}
Valid operations must supply a nested field set for any field that returns an object, so this operation is not valid:
Counter Example № 50{
name
relationship
}
However, this example is valid:
Example № 51{
name
relationship {
name
}
}
And will yield the subset of each object type queried:
Example № 52{
"name": "Mark Zuckerberg",
"relationship": {
"name": "Priscilla Chan"
}
}
Field Ordering
When querying an Object, the resulting mapping of fields are conceptually ordered in the same order in which they were encountered during execution, excluding fragments for which the type does not apply and fields or fragments that are skipped via @skip
or @include
directives or temporarily skipped via @defer
. This ordering is correctly produced when using the CollectFields() algorithm.
Response serialization formats capable of representing ordered maps should maintain this ordering. Serialization formats which can only represent unordered maps (such as JSON) should retain this order textually. That is, if two fields {foo, bar}
were queried in that order, the resulting JSON serialization should contain {"foo": "...", "bar": "..."}
in the same order.
Producing a response where fields are represented in the same order in which they appear in the request improves human readability during debugging and enables more efficient parsing of responses if the order of properties can be anticipated.
If a fragment is spread before other fields, the fields that fragment specifies occur in the response before the following fields.
Example № 53{
foo
...Frag
qux
}
fragment Frag on Query {
bar
baz
}
Produces the ordered result:
Example № 54{
"foo": 1,
"bar": 2,
"baz": 3,
"qux": 4
}
If a field is queried multiple times in a selection, it is ordered by the first time it is encountered. However fragments for which the type does not apply do not affect ordering.
Example № 55{
foo
...Ignored
...Matching
bar
}
fragment Ignored on UnknownType {
qux
baz
}
fragment Matching on Query {
bar
qux
foo
}
Produces the ordered result:
Example № 56{
"foo": 1,
"bar": 2,
"qux": 3
}
Also, if directives result in fields being excluded, they are not considered in the ordering of fields.
Example № 57{
foo @skip(if: true)
bar
foo
}
Produces the ordered result:
Example № 58{
"bar": 1,
"foo": 2
}
Result Coercion
Determining the result of coercing an object is the heart of the GraphQL executor, see Value Completion.
Input Coercion
Objects are never valid inputs.
Type Validation
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.
- An Object type must define one or more fields.
- For each field of an Object type:
- The field must have a unique name within that Object type; no two fields may share the same name.
- The field must not have a name which begins with the characters "__" (two underscores).
- The field must return a type where IsOutputType(fieldType) returns true.
- For each argument of the field:
- The argument must not have a name which begins with the characters "__" (two underscores).
- The argument must have a unique name within that field; no two arguments may share the same name.
- The argument must accept a type where IsInputType(argumentType) returns true.
- If argument type is Non-Null and a default value is not defined:
- The
@deprecated
directive must not be applied to this argument.
- The
- An object type may declare that it implements one or more unique interfaces.
- An object type must be a super-set of all interfaces it implements:
- Let this object type be objectType.
- For each interface declared implemented as interfaceType, IsValidImplementation(objectType, interfaceType) must be true.
- If implementedType declares it implements any interfaces, type must also declare it implements those interfaces.
- type must include a field of the same name for every field defined in implementedType.
- Let field be that named field on type.
- Let implementedField be that named field on implementedType.
- field must include an argument of the same name for every argument defined in implementedField.
- That named argument on field must accept the same type (invariant) as that named argument on implementedField.
- field may include additional arguments not defined in implementedField, but any additional argument must not be required, e.g. must not be of a non-nullable type.
- field must return a type which is equal to or a sub-type of (covariant) the return type of implementedField field’s return type:
- Let fieldType be the return type of field.
- Let implementedFieldType be the return type of implementedField.
- IsValidImplementationFieldType(fieldType, implementedFieldType) must be true.
- If fieldType is a Non-Null type:
- Let nullableType be the unwrapped nullable type of fieldType.
- Let implementedNullableType be the unwrapped nullable type of implementedFieldType if it is a Non-Null type, otherwise let it be implementedFieldType directly.
- Return IsValidImplementationFieldType(nullableType, implementedNullableType).
- If fieldType is a List type and implementedFieldType is also a List type:
- Let itemType be the unwrapped item type of fieldType.
- Let implementedItemType be the unwrapped item type of implementedFieldType.
- Return IsValidImplementationFieldType(itemType, implementedItemType).
- Return IsSubType(fieldType, implementedFieldType).
- If possibleSubType is the same type as superType then return true.
- If possibleSubType is an Object type and superType is a Union type and possibleSubType is a possible type of superType then return true.
- If possibleSubType is an Object or Interface type and superType is an Interface type and possibleSubType declares it implements superType then return true.
- Otherwise return false.
3.6.1Field Arguments
Object fields are conceptually functions which yield values. Occasionally object fields can accept arguments to further specify the return value. Object field arguments are defined as a list of all possible argument names and their expected input types.
All arguments defined within a field must not have a name which begins with "__" (two underscores), as this is used exclusively by GraphQL’s introspection system.
For example, a Person
type with a picture
field could accept an argument to determine what size of an image to return.
Example № 59type Person {
name: String
picture(size: Int): Url
}
Operations can optionally specify arguments to their fields to provide these arguments.
This example operation:
Example № 60{
name
picture(size: 600)
}
May return the result:
Example № 61{
"name": "Mark Zuckerberg",
"picture": "http://some.cdn/picture_600.jpg"
}
The type of an object field argument must be an input type (any type except an Object, Interface, or Union type).
3.6.2Field 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 ensure existing clients are not broken by the change), but the fields should be appropriately treated in documentation and tooling.
When using the type system definition language, @deprecated
directives are used to indicate that a field is deprecated:
Example № 62type ExampleType {
oldField: String @deprecated
}
3.6.3Object Extensions
Object type extensions are used to represent a type which has been extended from some original type. For example, this might be used to represent local data, or by a GraphQL service which is itself an extension of another GraphQL service.
In this example, a local data field is added to a Story
type:
Example № 63extend type Story {
isHiddenLocally: Boolean
}
Object type extensions may choose not to add additional fields, instead only adding interfaces or directives.
In this example, a directive is added to a User
type without adding fields:
Example № 64extend type User @addedDirective
Type Validation
Object type extensions have the potential to be invalid if incorrectly defined.
- The named type must already be defined and must be an Object type.
- The fields of an Object type extension must have unique names; no two fields may share the same name.
- Any fields of an Object type extension must not be already defined on the original Object type.
- Any non-repeatable directives provided must not already apply to the original Object type.
- Any interfaces provided must not be already implemented by the original Object type.
- The resulting extended object type must be a super-set of all interfaces it implements.
3.7Interfaces
GraphQL interfaces represent a list of named fields and their arguments. GraphQL objects and interfaces can then implement these interfaces which requires that the implementing type will define all fields defined by those interfaces.
Fields on a GraphQL interface have the same rules as fields on a GraphQL object; their type can be Scalar, Object, Enum, Interface, or Union, or any wrapping type whose base type is one of those five.
For example, an interface NamedEntity
may describe a required field and types such as Person
or Business
may then implement this interface to guarantee this field will always exist.
Types may also implement multiple interfaces. For example, Business
implements both the NamedEntity
and ValuedEntity
interfaces in the example below.
Example № 65interface NamedEntity {
name: String
}
interface ValuedEntity {
value: Int
}
type Person implements NamedEntity {
name: String
age: Int
}
type Business implements NamedEntity & ValuedEntity {
name: String
value: Int
employeeCount: Int
}
Fields which yield an interface are useful when one of many Object types are expected, but some fields should be guaranteed.
To continue the example, a Contact
might refer to NamedEntity
.
Example № 66type Contact {
entity: NamedEntity
phoneNumber: String
address: String
}
This allows us to write a selection set for a Contact
that can select the common fields.
Example № 67{
entity {
name
}
phoneNumber
}
When selecting fields on an interface type, only those fields declared on the interface may be queried. In the above example, entity
returns a NamedEntity
, and name
is defined on NamedEntity
, so it is valid. However, the following would not be a valid selection set against Contact
:
Counter Example № 68{
entity {
name
age
}
phoneNumber
}
because entity
refers to a NamedEntity
, and age
is not defined on that interface. Querying for age
is only valid when the result of entity
is a Person
; this can be expressed using a fragment or an inline fragment:
Example № 69{
entity {
name
... on Person {
age
}
}
phoneNumber
}
Interfaces Implementing Interfaces
When defining an interface that implements another interface, the implementing interface must define each field that is specified by the implemented interface. For example, the interface Resource must define the field id to implement the Node interface:
Example № 70interface Node {
id: ID!
}
interface Resource implements Node {
id: ID!
url: String
}
Transitively implemented interfaces (interfaces implemented by the interface that is being implemented) must also be defined on an implementing type or interface. For example, Image
cannot implement Resource
without also implementing Node
:
Example № 71interface Node {
id: ID!
}
interface Resource implements Node {
id: ID!
url: String
}
interface Image implements Resource & Node {
id: ID!
url: String
thumbnail: String
}
Interface definitions must not contain cyclic references nor implement themselves. This example is invalid because Node
and Named
implement themselves and each other:
Counter Example № 72interface Node implements Named & Node {
id: ID!
name: String
}
interface Named implements Node & Named {
id: ID!
name: String
}
Result Coercion
The interface type should have some way of determining which object a given result corresponds to. Once it has done so, the result coercion of the interface is the same as the result coercion of the object.
Input Coercion
Interfaces are never valid inputs.
Type Validation
Interface types have the potential to be invalid if incorrectly defined.
- An Interface type must define one or more fields.
- For each field of an Interface type:
- The field must have a unique name within that Interface type; no two fields may share the same name.
- The field must not have a name which begins with the characters "__" (two underscores).
- The field must return a type where IsOutputType(fieldType) returns true.
- For each argument of the field:
- The argument must not have a name which begins with the characters "__" (two underscores).
- The argument must have a unique name within that field; no two arguments may share the same name.
- The argument must accept a type where IsInputType(argumentType) returns true.
- An interface type may declare that it implements one or more unique interfaces, but may not implement itself.
- An interface type must be a super-set of all interfaces it implements:
- Let this interface type be implementingType.
- For each interface declared implemented as implementedType, IsValidImplementation(implementingType, implementedType) must be true.
3.7.1Interface Extensions
Interface type extensions are used to represent an interface which has been extended from some original interface. For example, this might be used to represent common local data on many types, or by a GraphQL service which is itself an extension of another GraphQL service.
In this example, an extended data field is added to a NamedEntity
type along with the types which implement it:
Example № 73extend interface NamedEntity {
nickname: String
}
extend type Person {
nickname: String
}
extend type Business {
nickname: String
}
Interface type extensions may choose not to add additional fields, instead only adding directives.
In this example, a directive is added to a NamedEntity
type without adding fields:
Example № 74extend interface NamedEntity @addedDirective
Type Validation
Interface type extensions have the potential to be invalid if incorrectly defined.
- The named type must already be defined and must be an Interface type.
- The fields of an Interface type extension must have unique names; no two fields may share the same name.
- Any fields of an Interface type extension must not be already defined on the original Interface type.
- Any Object or Interface type which implemented the original Interface type must also be a super-set of the fields of the Interface type extension (which may be due to Object type extension).
- Any non-repeatable directives provided must not already apply to the original Interface type.
- The resulting extended Interface type must be a super-set of all Interfaces it implements.
3.8Unions
GraphQL Unions represent an object that could be one of a list 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.
With interfaces and objects, only those fields defined on the type can be queried directly; to query other fields on an interface, typed fragments must be used. This is the same as for unions, but unions do not define any fields, so no fields may be queried on this type without the use of type refining fragments or inline fragments (with the exception of the meta-field __typename).
For example, we might define the following types:
Example № 75union SearchResult = Photo | Person
type Person {
name: String
age: Int
}
type Photo {
height: Int
width: Int
}
type SearchQuery {
firstSearchResult: SearchResult
}
In this example, a query operation wants the name if the result was a Person, and the height if it was a photo. However because a union itself defines no fields, this could be ambiguous and is invalid.
Counter Example № 76{
firstSearchResult {
name
height
}
}
A valid operation includes typed fragments (in this example, inline fragments):
Example № 77{
firstSearchResult {
... on Person {
name
}
... on Photo {
height
}
}
}
Union members may be defined with an optional leading |
character to aid formatting when representing a longer list of possible types:
Example № 78union SearchResult =
| Photo
| Person
Result Coercion
The union type should have some way of determining which object a given result corresponds to. Once it has done so, the result coercion of the union is the same as the result coercion of the object.
Input Coercion
Unions are never valid inputs.
Type Validation
Union types have the potential to be invalid if incorrectly defined.
- A Union type must include one or more unique member types.
- 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.
3.8.1Union Extensions
Union type extensions are used to represent a union type which has been extended from some original 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.
Type Validation
Union type extensions have the potential to be invalid if incorrectly defined.
- The named type must already be defined and must be a Union type.
- The member types of a Union type extension 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.
- All member types of a Union type extension must be unique.
- All member types of a Union type extension must not already be a member of the original Union type.
- Any non-repeatable directives provided must not already apply to the original Union type.
3.9Enums
GraphQL Enum types, like Scalar types, also represent leaf values in a GraphQL type system. However Enum types describe the set of possible values.
Enums are not references for a numeric value, but are unique values in their own right. They may serialize as a string: the name of the represented value.
In this example, an Enum type called Direction
is defined:
Example № 79enum Direction {
NORTH
EAST
SOUTH
WEST
}
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.
Input Coercion
GraphQL has a constant literal to represent enum input values. GraphQL string literals must not be accepted as an enum input and instead raise a request error.
Variable transport serializations which have a different representation for non-string symbolic values (for example, EDN) should only allow such values as enum input values. Otherwise, for most transport serializations that do not, strings may be interpreted as the enum input value with the same name.
Type Validation
Enum types have the potential to be invalid if incorrectly defined.
- An Enum type must define one or more unique enum values.
3.9.1Enum Extensions
Enum type extensions are used to represent an enum type which has been extended from some original enum 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.
Type Validation
Enum type extensions have the potential to be invalid if incorrectly defined.
- The named type must already be defined and must be an Enum type.
- All values of an Enum type extension must be unique.
- All values of an Enum type extension must not already be a value of the original Enum.
- Any non-repeatable directives provided must not already apply to the original Enum type.
3.10Input Objects
Fields may accept arguments to configure their behavior. These inputs are often scalars or enums, but they sometimes need to represent more complex values.
A GraphQL Input Object defines a set of input fields; the input fields are either scalars, enums, or other input objects. This allows arguments to accept arbitrarily complex structs.
In this example, an Input Object called Point2D
describes x
and y
inputs:
Example № 80input Point2D {
x: Float
y: Float
}
Circular References
Input Objects are allowed to reference other Input Objects as field types. A circular reference occurs when an Input Object references itself either directly or through referenced Input Objects.
Circular references are generally allowed, however they may not be defined as an unbroken chain of Non-Null singular fields. Such Input Objects are invalid because there is no way to provide a legal value for them.
This example of a circularly-referenced input type is valid as the field self
may be omitted or the value null.
Example № 81input Example {
self: Example
value: String
}
This example is also valid as the field self
may be an empty List.
Example № 82input Example {
self: [Example!]!
value: String
}
This example of a circularly-referenced input type is invalid as the field self
cannot be provided a finite value.
Counter Example № 83input Example {
value: String
self: Example!
}
This example is also invalid, as there is a non-null singular circular reference via the First.second
and Second.first
fields.
Counter Example № 84input First {
second: Second!
value: String
}
input Second {
first: First!
value: String
}
Result Coercion
An input object is never a valid result. Input Object types cannot be the return 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 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 is constructed with the following rules:
- If no value is provided for a defined input object field and that field definition provides a default value, the default value should be used. If no default value is provided and the input object field’s type is non-null, an error should be raised. Otherwise, if the field is not required, then no entry is added to the coerced unordered map.
- If the value null was provided for an input object field, and the field’s type is not a non-null type, an entry in the coerced unordered map is given the value null. In other words, there is a semantic difference between the explicitly provided value null versus having not provided a value.
- If a literal value is provided for an input object field, an entry in the coerced unordered map is given the result of coercing that value according to the input coercion rules for the type of that field.
- 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 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.
Following are examples of input coercion for an input object type with a String
field a
and a required (non-null) Int!
field b
:
Example № 85input ExampleInputObject {
a: String
b: Int!
}
Literal Value | Variables | Coerced Value |
---|---|---|
{ a: "abc", b: 123 } |
{} |
{ a: "abc", b: 123 } |
{ a: null, b: 123 } |
{} |
{ a: null, b: 123 } |
{ b: 123 } |
{} |
{ b: 123 } |
{ a: $var, b: 123 } |
{ var: null } |
{ a: null, b: 123 } |
{ a: $var, b: 123 } |
{} |
{ b: 123 } |
{ b: $var } |
{ var: 123 } |
{ b: 123 } |
$var |
{ var: { b: 123 } } |
{ b: 123 } |
"abc123" |
{} |
Error: Incorrect value |
$var |
{ var: "abc123" } |
Error: Incorrect value |
{ a: "abc", b: "123" } |
{} |
Error: Incorrect value for field b |
{ a: "abc" } |
{} |
Error: Missing required field b |
{ b: $var } |
{} |
Error: Missing required field b. |
$var |
{ var: { a: "abc" } } |
Error: Missing required field b |
{ a: "abc", b: null } |
{} |
Error: b must be non-null. |
{ b: $var } |
{ var: null } |
Error: b must be non-null. |
{ b: 123, c: "xyz" } |
{} |
Error: Unexpected field c |
Type Validation
- An Input Object type must define one or more input fields.
- For each input field of an Input Object type:
- The input field must have a unique name within that Input Object type; no two input fields may share the same name.
- The input field must not have a name which begins with the characters "__" (two underscores).
- The input field must accept a type where IsInputType(inputFieldType) returns true.
- If input field type is Non-Null and a default value is not defined:
- The
@deprecated
directive must not be applied to this input field.
- The
- 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.
3.10.1Input Object Extensions
Input object type extensions are used to represent an input object type which has been extended from some original input object type. For example, this might be used by a GraphQL service which is itself an extension of another GraphQL service.
Type Validation
Input object type extensions have the potential to be invalid if incorrectly defined.
- The named type must already be defined and must be a Input Object type.
- All fields of an Input Object type extension must have unique names.
- All fields of an Input Object type extension must not already be a field of the original Input Object.
- Any non-repeatable directives provided must not already apply to the original Input Object type.
3.11List
A GraphQL list is a special collection type which declares the type of each item in the List (referred to as the item type of the list). List values are serialized as ordered lists, where each item in the list is serialized as per the item type.
To denote that a field uses a List type the item type is wrapped in square brackets like this: pets: [Pet]
. Nesting lists is allowed: matrix: [[Int]]
.
Result Coercion
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.
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 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.
Input Coercion
When expected as an input, list values are accepted only when each item in the list can be accepted by the list’s item type.
If the value passed as an input to a list type is not a list and not the null value, then the result of input coercion is a list of size one, where the single item value is the result of input coercion for the list’s item type on the provided value (note this may apply recursively for nested lists).
This allows inputs which accept one or many arguments (sometimes referred to as “var args”) to declare their input type as a list while for the common case of a single value, a client can just pass that value directly rather than constructing the list.
Following are examples of input coercion with various list types and values:
Expected Type | Provided Value | Coerced Value |
---|---|---|
[Int] |
[1, 2, 3] |
[1, 2, 3] |
[Int] |
[1, "b", true] |
Error: Incorrect item value |
[Int] |
1 |
[1] |
[Int] |
null |
null |
[[Int]] |
[[1], [2, 3]] |
[[1], [2, 3]] |
[[Int]] |
[1, 2, 3] |
Error: Incorrect item value |
[[Int]] |
1 |
[[1]] |
[[Int]] |
null |
null |
3.12Non-Null
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 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 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, 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. 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.
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.
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.
A non-null argument cannot be omitted:
Counter Example № 86{
fieldWithNonNullArg
}
The value null cannot be provided to a non-null argument:
Counter Example № 87{
fieldWithNonNullArg(nonNullArg: null)
}
A variable of a nullable type cannot be provided to a non-null argument:
Example № 88query withNullableVariable($var: String) {
fieldWithNonNullArg(nonNullArg: $var)
}
Type Validation
- A Non-Null type must not wrap another Non-Null type.
3.12.1Combining List and Non-Null
The List and Non-Null wrapping types can compose, representing more complex types. The rules for result coercion and input coercion of Lists and Non-Null types apply in a recursive fashion.
For example if the inner item type of a List is Non-Null (e.g. [T!]
), then that List may not contain any null items. However if the inner type of a Non-Null is a List (e.g. [T]!
), then null is not accepted however an empty list is accepted.
Following are examples of result coercion with various types and values:
Expected Type | Internal Value | Coerced Result |
---|---|---|
[Int] |
[1, 2, 3] |
[1, 2, 3] |
[Int] |
null |
null |
[Int] |
[1, 2, null] |
[1, 2, null] |
[Int] |
[1, 2, Error] |
[1, 2, null] (With logged error) |
[Int]! |
[1, 2, 3] |
[1, 2, 3] |
[Int]! |
null |
Error: Value cannot be null |
[Int]! |
[1, 2, null] |
[1, 2, null] |
[Int]! |
[1, 2, Error] |
[1, 2, null] (With logged error) |
[Int!] |
[1, 2, 3] |
[1, 2, 3] |
[Int!] |
null |
null |
[Int!] |
[1, 2, null] |
null (With logged coercion error) |
[Int!] |
[1, 2, Error] |
null (With logged error) |
[Int!]! |
[1, 2, 3] |
[1, 2, 3] |
[Int!]! |
null |
Error: Value cannot be null |
[Int!]! |
[1, 2, null] |
Error: Item cannot be null |
[Int!]! |
[1, 2, Error] |
Error: Error occurred in item |
3.13Directives
QUERY |
MUTATION |
SUBSCRIPTION |
FIELD |
FRAGMENT_DEFINITION |
FRAGMENT_SPREAD |
INLINE_FRAGMENT |
VARIABLE_DEFINITION |
SCHEMA |
SCALAR |
OBJECT |
FIELD_DEFINITION |
ARGUMENT_DEFINITION |
INTERFACE |
UNION |
ENUM |
ENUM_VALUE |
INPUT_OBJECT |
INPUT_FIELD_DEFINITION |
A GraphQL schema describes directives which are used to annotate various parts of a GraphQL document as an indicator that they should be evaluated differently by a validator, executor, or client tool such as a code generator.
Built-in Directives
A built-in directive is any directive defined within this specification.
GraphQL implementations should provide the @skip
and @include
directives.
GraphQL implementations are not required to implement the @defer
and @stream
directives. If either or both of these directives are implemented, they must be implemented according to this specification. GraphQL implementations that do not support these directives must not make them available via introspection.
GraphQL implementations that support the type system definition language must provide the @deprecated
directive if representing deprecated portions of the schema.
GraphQL implementations that support the type system definition language should provide the @specifiedBy
directive if representing custom scalar definitions.
When representing a GraphQL schema using the type system definition language any built-in directive may be omitted for brevity.
When introspecting a GraphQL service all provided directives, including any built-in directive, must be included in the set of returned directives.
Custom Directives
GraphQL services and client tooling may provide any additional custom directive beyond those defined in this document. Directives are the preferred way to extend GraphQL with custom or experimental behavior.
_
in their name). For example, a custom directive used by Facebook’s GraphQL service should be named @fb_auth
instead of @auth
. This is especially recommended for proposed additions to this specification which can change during the RFC process. For example a work in progress version of @live
should be named @rfc_live
.Directives must only be used in the locations they are declared to belong in. In this example, a directive is defined which can be used to annotate a field:
Example № 89directive @example on FIELD
fragment SomeFragment on SomeType {
field @example
}
Directive locations may be defined with an optional leading |
character to aid formatting when representing a longer list of possible locations:
Example № 90directive @example on
| FIELD
| FRAGMENT_SPREAD
| INLINE_FRAGMENT
Directives can also be used to annotate the type system definition language as well, which can be a useful tool for supplying additional metadata in order to generate GraphQL execution services, produce client generated runtime code, or many other useful extensions of the GraphQL semantics.
In this example, the directive @example
annotates field and argument definitions:
Example № 91directive @example on FIELD_DEFINITION | ARGUMENT_DEFINITION
type SomeType {
field(arg: Int @example): String @example
}
A directive may be defined as repeatable by including the “repeatable” keyword. Repeatable directives are often useful when the same directive should be used with different arguments at a single location, especially in cases where additional information needs to be provided to a type or schema extension via a directive:
Example № 92directive @delegateField(name: String!) repeatable on OBJECT | INTERFACE
type Book @delegateField(name: "pageCount") @delegateField(name: "author") {
id: ID!
}
extend type Book @delegateField(name: "index")
While defining a directive, it must not reference itself directly or indirectly:
Counter Example № 93directive @invalidExample(arg: String @invalidExample) on ARGUMENT_DEFINITION
Validation
- A directive definition must not contain the use of a directive which references itself directly.
- A directive definition must not contain the use of a directive which references itself indirectly by referencing a Type or Directive which transitively includes a reference to this directive.
- The directive must not have a name which begins with the characters "__" (two underscores).
- For each argument of the directive:
- The argument must not have a name which begins with the characters "__" (two underscores).
- The argument must have a unique name within that directive; no two arguments may share the same name.
- The argument must accept a type where IsInputType(argumentType) returns true.
3.13.1@skip
directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
The @skip
built-in directive may be provided for fields, fragment spreads, and inline fragments, and allows for conditional exclusion during execution as described by the if
argument.
In this example experimentalField
will only be queried if the variable $someTest
has the value false
.
Example № 94query myQuery($someTest: Boolean!) {
experimentalField @skip(if: $someTest)
}
3.13.2@include
directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
The @include
built-in directive may be provided for fields, fragment spreads, and inline fragments, and allows for conditional inclusion during execution as described by the if
argument.
In this example experimentalField
will only be queried if the variable $someTest
has the value true
Example № 95query myQuery($someTest: Boolean!) {
experimentalField @include(if: $someTest)
}
@skip
nor @include
has precedence over the other. In the case that both the @skip
and @include
directives are provided on the same field or fragment, it must be queried only if the @skip
condition is false and the @include
condition is true. Stated conversely, the field or fragment must not be queried if either the @skip
condition is true or the @include
condition is false.3.13.3@deprecated
directive @deprecated(
reason: String = "No longer supported"
) 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, 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).
In this example type definition, oldField
is deprecated in favor of using newField
and oldArg
is deprecated in favor of using newArg
.
Example № 96type 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.
Counter Example № 97type 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.
3.13.4@specifiedBy
directive @specifiedBy(url: String!) on SCALAR
The @specifiedBy
built-in directive is used within the type system definition language to provide a scalar specification URL for specifying the behavior of custom scalar types. 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.
In this example, a custom scalar type for UUID
is defined with a URL pointing to the relevant IETF specification.
Example № 98scalar UUID @specifiedBy(url: "https://tools.ietf.org/html/rfc4122")
3.13.5@defer
directive @defer(
label: String
if: Boolean! = true
) on FRAGMENT_SPREAD | INLINE_FRAGMENT
The @defer
directive may be provided for fragment spreads and inline fragments to inform the executor to delay the execution of the current fragment to indicate deprioritization of the current fragment. A query with @defer
directive will cause the request to potentially return multiple responses, where non-deferred data is delivered in the initial response and data deferred is delivered in a subsequent response. @include
and @skip
take precedence over @defer
.
Example № 99query myQuery($shouldDefer: Boolean) {
user {
name
...someFragment @defer(label: "someLabel", if: $shouldDefer)
}
}
fragment someFragment on User {
id
profile_picture {
uri
}
}
3.13.5.1@defer Arguments
if: Boolean! = true
- Whentrue
, fragment should be deferred (see related note below). Whenfalse
, fragment will not be deferred and data will be included in the initial response. Defaults totrue
when omitted.label: String
- May be used by GraphQL clients to identify the data from responses and associate it with the corresponding defer directive. If provided, the GraphQL service must add it to the corresponding pending object in the response.label
must be unique label across all@defer
and@stream
directives in a document.label
must not be provided as a variable.
3.13.6@stream
directive @stream(
label: String
if: Boolean! = true
initialCount: Int = 0
) on FIELD
The @stream
directive may be provided for a field of List
type so that the backend can leverage technology such as asynchronous iterators to provide a partial list in the initial response, and additional list items in subsequent responses. @include
and @skip
take precedence over @stream
.
Example № 100query myQuery($shouldStream: Boolean) {
user {
friends(first: 10) {
nodes @stream(label: "friendsStream", initialCount: 5, if: $shouldStream)
}
}
}
3.13.6.1@stream Arguments
if: Boolean! = true
- Whentrue
, field should be streamed (see related note below). Whenfalse
, the field will not be streamed and all list items will be included in the initial response. Defaults totrue
when omitted.label: String
- May be used by GraphQL clients to identify the data from responses and associate it with the corresponding stream directive. If provided, the GraphQL service must add it to the corresponding pending object in the response.label
must be unique label across all@defer
and@stream
directives in a document.label
must not be provided as a variable.initialCount: Int
- The number of list items the service should return as part of the initial response. If omitted, defaults to0
. A field error will be raised if the value of this argument is less than0
.
@defer
and @stream
directives on each execution. However, the specification allows advanced use cases where the service can determine that it is more performant to not defer and/or stream. Therefore, GraphQL clients must be able to process a response that ignores the @defer
and/or @stream
directives. This also applies to the initialCount
argument on the @stream
directive. Clients must be able to process a streamed response that contains a different number of initial list items than what was specified in the initialCount
argument. 4Introspection
A GraphQL service supports introspection over its schema. This schema is queried using GraphQL itself, creating a powerful platform for tool-building.
Take an example request for a trivial app. In this case there is a User type with three fields: id, name, and birthday.
For example, given a service with the following type definition:
Example № 101type User {
id: String
name: String
birthday: Date
}
A request containing the operation:
Example № 102{
__type(name: "User") {
name
fields {
name
type {
name
}
}
}
}
would produce the result:
Example № 103{
"__type": {
"name": "User",
"fields": [
{
"name": "id",
"type": { "name": "String" }
},
{
"name": "name",
"type": { "name": "String" }
},
{
"name": "birthday",
"type": { "name": "Date" }
}
]
}
}
Reserved Names
Types and fields required by the GraphQL introspection system that are used in the same context as user-defined types and fields are prefixed with "__" two underscores. This in order to avoid naming collisions with user-defined GraphQL types.
Otherwise, any Name within a GraphQL type system must not start with two underscores "__".
4.1Type Name Introspection
GraphQL supports type name introspection within any selection set in an operation, with the single exception of selections at the root of a subscription operation. Type name introspection is accomplished via the meta-field __typename: String!
on any Object, Interface, or Union. It returns the name of the concrete Object type at that point during execution.
This is most often used when querying against Interface or Union types to identify which actual Object type of the possible types has been returned.
As a meta-field, __typename
is implicit and does not appear in the fields list in any defined type.
__typename
may not be included as a root field in a subscription operation.4.2Schema 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.
__schema: __Schema!
__type(name: String!): __Type
Like all meta-fields, these are implicit and do not appear in the fields list in the root type of the query operation.
First Class Documentation
All types in the introspection system provide a description
field of type String
to allow type designers to publish documentation in addition to capabilities. A GraphQL service may return the description
field using Markdown syntax (as specified by CommonMark). Therefore it is recommended that any tool that displays description
use a CommonMark-compliant Markdown renderer.
Deprecation
To support the management of backwards compatibility, GraphQL fields, arguments, input fields, and enum values can indicate whether or not they are deprecated (isDeprecated: Boolean
) along with a description of why it is deprecated (deprecationReason: String
).
Tools built using GraphQL introspection should respect deprecation by discouraging deprecated use through information hiding or developer-facing warnings.
Schema Introspection Schema
The schema introspection system is itself represented as a GraphQL schema. Below are the full set of type system definitions providing schema introspection, which are fully defined in the sections below.
type __Schema {
description: String
types: [__Type!]!
queryType: __Type!
mutationType: __Type
subscriptionType: __Type
directives: [__Directive!]!
}
type __Type {
kind: __TypeKind!
name: String
description: String
# must be non-null for OBJECT and INTERFACE, otherwise null.
fields(includeDeprecated: Boolean = false): [__Field!]
# must be non-null for OBJECT and INTERFACE, otherwise null.
interfaces: [__Type!]
# must be non-null for INTERFACE and UNION, otherwise null.
possibleTypes: [__Type!]
# must be non-null for ENUM, otherwise null.
enumValues(includeDeprecated: Boolean = false): [__EnumValue!]
# must be non-null for INPUT_OBJECT, otherwise null.
inputFields(includeDeprecated: Boolean = false): [__InputValue!]
# must be non-null for NON_NULL and LIST, otherwise null.
ofType: __Type
# may be non-null for custom SCALAR, otherwise null.
specifiedByURL: String
}
enum __TypeKind {
SCALAR
OBJECT
INTERFACE
UNION
ENUM
INPUT_OBJECT
LIST
NON_NULL
}
type __Field {
name: String!
description: String
args(includeDeprecated: Boolean = false): [__InputValue!]!
type: __Type!
isDeprecated: Boolean!
deprecationReason: String
}
type __InputValue {
name: String!
description: String
type: __Type!
defaultValue: String
isDeprecated: Boolean!
deprecationReason: String
}
type __EnumValue {
name: String!
description: String
isDeprecated: Boolean!
deprecationReason: String
}
type __Directive {
name: String!
description: String
locations: [__DirectiveLocation!]!
args(includeDeprecated: Boolean = false): [__InputValue!]!
isRepeatable: Boolean!
}
enum __DirectiveLocation {
QUERY
MUTATION
SUBSCRIPTION
FIELD
FRAGMENT_DEFINITION
FRAGMENT_SPREAD
INLINE_FRAGMENT
VARIABLE_DEFINITION
SCHEMA
SCALAR
OBJECT
FIELD_DEFINITION
ARGUMENT_DEFINITION
INTERFACE
UNION
ENUM
ENUM_VALUE
INPUT_OBJECT
INPUT_FIELD_DEFINITION
}
4.2.1The __Schema Type
The __Schema
type is returned from the __schema
meta-field and provides all information about the schema of a GraphQL service.
Fields:
description
may return a String or null.queryType
is the root type of a query operation.mutationType
is the root type of a mutation operation, if supported. Otherwise null.subscriptionType
is the root type of a subscription operation, if supported. Otherwise null.types
must return the set of all named types contained within this schema. Any named type which can be found through a field of any introspection type must be included in this set.directives
must return the set of all directives available within this schema including all built-in directives.
4.2.2The __Type Type
__Type
is at the core of the type introspection system, it represents all types in the system: both named types (e.g. Scalars and Object types) and type modifiers (e.g. List and Non-Null types).
Type modifiers are used to modify the type presented in the field ofType
. This modified type may recursively be a modified type, representing lists, non-nullables, and combinations thereof, ultimately modifying a named type.
There are several different kinds of type. In each kind, different fields are actually valid. All possible kinds are listed in the __TypeKind
enum.
Each sub-section below defines the expected fields of __Type
given each possible value of the __TypeKind
enum:
- "SCALAR"
- "OBJECT"
- "INTERFACE"
- "UNION"
- "ENUM"
- "INPUT_OBJECT"
- "LIST"
- "NON_NULL"
Scalar
Represents scalar types such as Int, String, and Boolean. Scalars cannot have fields.
Also represents Custom scalars which may provide specifiedByURL
as a scalar specification URL.
Fields:
kind
must return__TypeKind.SCALAR
.name
must return a String.description
may return a String or null.specifiedByURL
may return a String (in the form of a URL) for custom scalars, otherwise must be null.- All other fields must return null.
Object
Object types represent concrete instantiations of sets of fields. The introspection types (e.g. __Type
, __Field
, etc) are examples of objects.
Fields:
kind
must return__TypeKind.OBJECT
.name
must return a String.description
may return a String or null.fields
must return the set of fields that can be selected for this type.- Accepts the argument
includeDeprecated
which defaults to false. If true, deprecated fields are also returned.
- Accepts the argument
interfaces
must return the set of interfaces that an object implements (if none,interfaces
must return the empty set).- All other fields must return null.
Union
Unions are an abstract type where no common fields are declared. The possible types of a union are explicitly listed out in possibleTypes
. Types can be made parts of unions without modification of that type.
Fields:
kind
must return__TypeKind.UNION
.name
must return a String.description
may return a String or null.possibleTypes
returns the list of types that can be represented within this union. They must be object types.- All other fields must return null.
Interface
Interfaces are an abstract type where there are common fields declared. Any type that implements an interface must define all the named fields where each implementing field type is equal to or a sub-type of (covariant) the interface type. The implementations of this interface are explicitly listed out in possibleTypes
.
Fields:
kind
must return__TypeKind.INTERFACE
.name
must return a String.description
may return a String or null.fields
must return the set of fields required by this interface.- Accepts the argument
includeDeprecated
which defaults to false. If true, deprecated fields are also returned.
- Accepts the argument
interfaces
must return the set of interfaces that an interface implements (if none,interfaces
must return the empty set).possibleTypes
returns the list of types that implement this interface. They must be object types.- All other fields must return null.
Enum
Enums are special scalars that can only have a defined set of values.
Fields:
kind
must return__TypeKind.ENUM
.name
must return a String.description
may return a String or null.enumValues
must return the set of enum values as a list of__EnumValue
. There must be at least one and they must have unique names.- Accepts the argument
includeDeprecated
which defaults to false. If true, deprecated enum values are also returned.
- Accepts the argument
- All other fields must return null.
Input Object
Input objects are composite types defined as a list of named input values. They are only used as inputs to arguments and variables and cannot be a field return type.
For example the input object Point
could be defined as:
Example № 104input Point {
x: Int
y: Int
}
Fields:
kind
must return__TypeKind.INPUT_OBJECT
.name
must return a String.description
may return a String or null.inputFields
must return the set of input fields as a list of__InputValue
.- Accepts the argument
includeDeprecated
which defaults to false. If true, deprecated input fields are also returned.
- Accepts the argument
- All other fields must return null.
List
Lists represent sequences of values in GraphQL. A List type is a type modifier: it wraps another type instance in the ofType
field, which defines the type of each item in the list.
The modified type in the ofType
field may itself be a modified type, allowing the representation of Lists of Lists, or Lists of Non-Nulls.
Fields:
kind
must return__TypeKind.LIST
.ofType
must return a type of any kind.- All other fields must return null.
Non-Null
GraphQL types are nullable. The value null is a valid response for field type.
A Non-Null type is a type modifier: it wraps another type instance in the ofType
field. Non-null types do not allow null as a response, and indicate required inputs for arguments and input object fields.
The modified type in the ofType
field may itself be a modified List type, allowing the representation of Non-Null of Lists. However it must not be a modified Non-Null type to avoid a redundant Non-Null of Non-Null.
Fields:
kind
must return__TypeKind.NON_NULL
.ofType
must return a type of any kind except Non-Null.- All other fields must return null.
4.2.3The __Field Type
The __Field
type represents each field in an Object or Interface type.
Fields:
name
must return a String.description
may return a String or null.args
returns a List of__InputValue
representing the arguments this field accepts.- Accepts the argument
includeDeprecated
which defaults to false. If true, deprecated arguments are also returned.
- Accepts the argument
type
must return a__Type
that represents the type of value returned by this field.isDeprecated
returns true if this field should no longer be used, otherwise false.deprecationReason
optionally provides a reason why this field is deprecated.
4.2.4The __InputValue Type
The __InputValue
type represents field and directive arguments as well as the inputFields
of an input object.
Fields:
name
must return a String.description
may return a String or null.type
must return a__Type
that represents the type this input value expects.defaultValue
may return a String encoding (using the GraphQL language) of the default value used by this input value in the condition a value is not provided at runtime. If this input value has no default value, returns null.isDeprecated
returns true if this input field or argument should no longer be used, otherwise false.deprecationReason
optionally provides a reason why this input field or argument is deprecated.
4.2.5The __EnumValue Type
The __EnumValue
type represents one of possible values of an enum.
Fields:
name
must return a String.description
may return a String or null.isDeprecated
returns true if this enum value should no longer be used, otherwise false.deprecationReason
optionally provides a reason why this enum value is deprecated.
4.2.6The __Directive Type
The __Directive
type represents a directive that a service supports.
This includes both any built-in directive and any custom directive.
Individual directives may only be used in locations that are explicitly supported. All possible locations are listed in the __DirectiveLocation
enum:
- "QUERY"
- "MUTATION"
- "SUBSCRIPTION"
- "FIELD"
- "FRAGMENT_DEFINITION"
- "FRAGMENT_SPREAD"
- "INLINE_FRAGMENT"
- "VARIABLE_DEFINITION"
- "SCHEMA"
- "SCALAR"
- "OBJECT"
- "FIELD_DEFINITION"
- "ARGUMENT_DEFINITION"
- "INTERFACE"
- "UNION"
- "ENUM"
- "ENUM_VALUE"
- "INPUT_OBJECT"
- "INPUT_FIELD_DEFINITION"
Fields:
name
must return a String.description
may return a String or null.locations
returns a List of__DirectiveLocation
representing the valid locations this directive may be placed.args
returns a List of__InputValue
representing the arguments this directive accepts.- Accepts the argument
includeDeprecated
which defaults to false. If true, deprecated arguments are also returned.
- Accepts the argument
isRepeatable
must return a Boolean that indicates if the directive may be used repeatedly at a single location.
5Validation
A GraphQL service does not just verify if a request is syntactically correct, but also ensures that it is unambiguous and mistake-free in the context of a given GraphQL schema.
An invalid request is still technically executable, and will always produce a stable result as defined by the algorithms in the Execution section, however that result may be ambiguous, surprising, or unexpected relative to a request containing validation errors, so execution should only occur for valid requests.
Typically validation is performed in the context of a request immediately before execution, however a GraphQL service may execute a request without explicitly validating it if that exact same request is known to have been validated before. For example: the request may be validated during development, provided it does not later change, or a service may validate a request once and memoize the result to avoid validating the same request again in the future. Any client-side or development-time tool should report validation errors and not allow the formulation or execution of requests known to be invalid at that given point in time.
Type System Evolution
As GraphQL type system schema evolves over time by adding new types and new fields, it is possible that a request which was previously valid could later become invalid. Any change that can cause a previously valid request to become invalid is considered a breaking change. GraphQL services and schema maintainers are encouraged to avoid breaking changes, however in order to be more resilient to these breaking changes, sophisticated GraphQL systems may still allow for the execution of requests which at some point were known to be free of any validation errors, and have not changed since.
Examples
For this section of this schema, we will assume the following type system in order to demonstrate examples:
Example № 105type Query {
dog: Dog
findDog(searchBy: FindDogInput): Dog
}
enum DogCommand {
SIT
DOWN
HEEL
}
type Dog implements Pet {
name: String!
nickname: String
barkVolume: Int
doesKnowCommand(dogCommand: DogCommand!): Boolean!
isHouseTrained(atOtherHomes: Boolean): Boolean!
owner: Human
}
interface Sentient {
name: String!
}
interface Pet {
name: String!
}
type Alien implements Sentient {
name: String!
homePlanet: String
}
type Human implements Sentient {
name: String!
pets: [Pet!]
}
enum CatCommand {
JUMP
}
type Cat implements Pet {
name: String!
nickname: String
doesKnowCommand(catCommand: CatCommand!): Boolean!
meowVolume: Int
}
union CatOrDog = Cat | Dog
union DogOrHuman = Dog | Human
union HumanOrAlien = Human | Alien
input FindDogInput {
name: String
owner: String
}
5.1Documents
5.1.1Executable Definitions
Formal Specification
- For each definition definition in the document:
- definition must be ExecutableDefinition (it must not be TypeSystemDefinitionOrExtension).
Explanatory Text
GraphQL execution will only consider the executable definitions Operation and Fragment. Type system definitions and extensions are not executable, and are not considered during execution.
To avoid ambiguity, a document containing TypeSystemDefinitionOrExtension is invalid for execution.
GraphQL documents not intended to be directly executed may include TypeSystemDefinitionOrExtension.
For example, the following document is invalid for execution since the original executing schema may not know about the provided type extension:
Counter Example № 106query getDogName {
dog {
name
color
}
}
extend type Dog {
color: String
}
5.2Operations
5.2.1Named Operation Definitions
5.2.1.1Operation Name Uniqueness
Formal Specification
- For each operation definition operation in the document:
- Let operationName be the name of operation.
- If operationName exists:
- Let operations be all operation definitions in the document named operationName.
- operations must be a set of one.
Explanatory Text
Each named operation definition must be unique within a document when referred to by its name.
For example the following document is valid:
Example № 107query getDogName {
dog {
name
}
}
query getOwnerName {
dog {
owner {
name
}
}
}
While this document is invalid:
Counter Example № 108query getName {
dog {
name
}
}
query getName {
dog {
owner {
name
}
}
}
It is invalid even if the type of each operation is different:
Counter Example № 109query dogOperation {
dog {
name
}
}
mutation dogOperation {
mutateDog {
id
}
}
5.2.2Anonymous Operation Definitions
5.2.2.1Lone Anonymous Operation
Formal Specification
- Let operations be all operation definitions in the document.
- Let anonymous be all anonymous operation definitions in the document.
- If operations is a set of more than 1:
- anonymous must be empty.
Explanatory Text
GraphQL allows a short-hand form for defining query operations when only that one operation exists in the document.
For example the following document is valid:
Example № 110{
dog {
name
}
}
While this document is invalid:
Counter Example № 111{
dog {
name
}
}
query getName {
dog {
owner {
name
}
}
}
5.2.3Subscription Operation Definitions
5.2.3.1Single Root Field
Formal Specification
- Let subscriptionType be the root Subscription type in schema.
- For each subscription operation definition subscription in the document:
- Let selectionSet be the top level selection set on subscription.
- Let variableValues be the empty set.
- Let groupedFieldSet be the result of CollectFields(subscriptionType, selectionSet, variableValues).
- groupedFieldSet must have exactly one entry, which must not be an introspection field.
Explanatory Text
Subscription operations must have exactly one root field.
Valid examples:
Example № 112subscription sub {
newMessage {
body
sender
}
}
Example № 113subscription sub {
...newMessageFields
}
fragment newMessageFields on Subscription {
newMessage {
body
sender
}
}
Invalid:
Counter Example № 114subscription sub {
newMessage {
body
sender
}
disallowedSecondRootField
}
Counter Example № 115subscription sub {
...multipleSubscriptions
}
fragment multipleSubscriptions on Subscription {
newMessage {
body
sender
}
disallowedSecondRootField
}
The root field of a subscription operation must not be an introspection field. The following example is also invalid:
Counter Example № 116subscription sub {
__typename
}
5.3Fields
5.3.1Field Selections
Field selections must exist on Object, Interface, and Union types.
Formal Specification
- For each selection in the document:
- Let fieldName be the target field of selection.
- fieldName must be defined on type in scope.
Explanatory Text
The target field of a field selection must be defined on the scoped type of the selection set. There are no limitations on alias names.
For example the following fragment would not pass validation:
Counter Example № 117fragment fieldNotDefined on Dog {
meowVolume
}
fragment aliasedLyingFieldTargetNotDefined on Dog {
barkVolume: kawVolume
}
For interfaces, direct field selection can only be done on fields. Fields of concrete implementers are not relevant to the validity of the given interface-typed selection set.
For example, the following is valid:
Example № 118fragment interfaceFieldSelection on Pet {
name
}
and the following is invalid:
Counter Example № 119fragment definedOnImplementersButNotInterface on Pet {
nickname
}
Because unions do not define fields, fields may not be directly selected from a union-typed selection set, with the exception of the meta-field __typename. Fields from a union-typed selection set must only be queried indirectly via a fragment.
For example the following is valid:
Example № 120fragment inDirectFieldSelectionOnUnion on CatOrDog {
__typename
... on Pet {
name
}
... on Dog {
barkVolume
}
}
But the following is invalid:
Counter Example № 121fragment directFieldSelectionOnUnion on CatOrDog {
name
barkVolume
}
5.3.2Field Selection Merging
Formal Specification
- Let set be any selection set defined in the GraphQL document.
- FieldsInSetCanMerge(set) must be true.
- Let fieldsForName be the set of selections with a given response name in set including visiting fragments and inline fragments.
- Given each pair of members fieldA and fieldB in fieldsForName:
- SameResponseShape(fieldA, fieldB) must be true.
- SameStreamDirective(fieldA, fieldB) must be true.
- If the parent types of fieldA and fieldB are equal or if either is not an Object Type:
- fieldA and fieldB must have identical field names.
- fieldA and fieldB must have identical sets of arguments.
- Let mergedSet be the result of adding the selection set of fieldA and the selection set of fieldB.
- FieldsInSetCanMerge(mergedSet) must be true.
- Let typeA be the return type of fieldA.
- Let typeB be the return type of fieldB.
- If typeA or typeB is Non-Null:
- If typeA or typeB is nullable, return false.
- Let typeA be the nullable type of typeA.
- Let typeB be the nullable type of typeB.
- If typeA or typeB is List:
- If typeA or typeB is not List, return false.
- Let typeA be the item type of typeA.
- Let typeB be the item type of typeB.
- Repeat from step 3.
- If typeA or typeB is Scalar or Enum:
- If typeA and typeB are the same type return true, otherwise return false.
- Assert: typeA is an object, union or interface type.
- Assert: typeB is an object, union or interface type.
- Let mergedSet be the result of adding the selection set of fieldA and the selection set of fieldB.
- Let fieldsForName be the set of selections with a given response name in mergedSet including visiting fragments and inline fragments.
- Given each pair of members subfieldA and subfieldB in fieldsForName:
- If SameResponseShape(subfieldA, subfieldB) is false, return false.
- Return true.
- If neither fieldA nor fieldB has a directive named
stream
.- Return true.
- If both fieldA and fieldB have a directive named
stream
.- Let streamA be the directive named
stream
on fieldA. - Let streamB be the directive named
stream
on fieldB. - If streamA and streamB have identical sets of arguments, return true.
- Let streamA be the directive named
- Return false.
Explanatory Text
If multiple field selections with the same response names are encountered during execution, the field and arguments to execute and the resulting value should be unambiguous. Therefore any two field selections which might both be encountered for the same object are only valid if they are equivalent.
During execution, the simultaneous execution of fields with the same response name is accomplished by CollectSubfields().
For simple hand-written GraphQL, this rule is obviously a clear developer error, however nested fragments can make this difficult to detect manually.
The following selections correctly merge:
Example № 122fragment mergeIdenticalFields on Dog {
name
name
}
fragment mergeIdenticalAliasesAndFields on Dog {
otherName: name
otherName: name
}
The following is not able to merge:
Counter Example № 123fragment conflictingBecauseAlias on Dog {
name: nickname
name
}
Identical fields are also merged if they have identical arguments. Both values and variables can be correctly merged.
For example the following correctly merge:
Example № 124fragment mergeIdenticalFieldsWithIdenticalArgs on Dog {
doesKnowCommand(dogCommand: SIT)
doesKnowCommand(dogCommand: SIT)
}
fragment mergeIdenticalFieldsWithIdenticalValues on Dog {
doesKnowCommand(dogCommand: $dogCommand)
doesKnowCommand(dogCommand: $dogCommand)
}
The following do not correctly merge:
Counter Example № 125fragment conflictingArgsOnValues on Dog {
doesKnowCommand(dogCommand: SIT)
doesKnowCommand(dogCommand: HEEL)
}
fragment conflictingArgsValueAndVar on Dog {
doesKnowCommand(dogCommand: SIT)
doesKnowCommand(dogCommand: $dogCommand)
}
fragment conflictingArgsWithVars on Dog {
doesKnowCommand(dogCommand: $varOne)
doesKnowCommand(dogCommand: $varTwo)
}
fragment differingArgs on Dog {
doesKnowCommand(dogCommand: SIT)
doesKnowCommand
}
The following fields would not merge together, however both cannot be encountered against the same object, so they are safe:
Example № 126fragment safeDifferingFields on Pet {
... on Dog {
volume: barkVolume
}
... on Cat {
volume: meowVolume
}
}
fragment safeDifferingArgs on Pet {
... on Dog {
doesKnowCommand(dogCommand: SIT)
}
... on Cat {
doesKnowCommand(catCommand: JUMP)
}
}
However, the field responses must be shapes which can be merged. For example, leaf types must not differ. In this example, someValue
might be a String
or an Int
:
Counter Example № 127fragment conflictingDifferingResponses on Pet {
... on Dog {
someValue: nickname
}
... on Cat {
someValue: meowVolume
}
}
5.3.3Leaf Field Selections
Formal Specification
- For each selection in the document:
- Let selectionType be the unwrapped result type of selection.
- If selectionType is a scalar or enum:
- The subselection set of that selection must be empty.
- If selectionType is an interface, union, or object:
- The subselection set of that selection must not be empty.
Explanatory Text
A field subselection is not allowed on leaf fields. A leaf field is any field with a scalar or enum unwrapped type.
The following is valid.
Example № 128fragment scalarSelection on Dog {
barkVolume
}
The following is invalid.
Counter Example № 129fragment scalarSelectionsNotAllowedOnInt on Dog {
barkVolume {
sinceWhen
}
}
Conversely, non-leaf fields must have a field subselection. A non-leaf field is any field with an object, interface, or union unwrapped type.
Let’s assume the following additions to the query root operation type of the schema:
Example № 130extend type Query {
human: Human
pet: Pet
catOrDog: CatOrDog
}
The following examples are invalid because they include non-leaf fields without a field subselection.
Counter Example № 131query directQueryOnObjectWithoutSubFields {
human
}
query directQueryOnInterfaceWithoutSubFields {
pet
}
query directQueryOnUnionWithoutSubFields {
catOrDog
}
However the following example is valid since it includes a field subselection.
Example № 132query directQueryOnObjectWithSubFields {
human {
name
}
}
5.4Arguments
Arguments are provided to both fields and directives. The following validation rules apply in both cases.
5.4.1Argument Names
Formal Specification
- For each argument in the document:
- Let argumentName be the Name of argument.
- Let argumentDefinition be the argument definition provided by the parent field or definition named argumentName.
- argumentDefinition must exist.
Explanatory Text
Every argument provided to a field or directive must be defined in the set of possible arguments of that field or directive.
For example the following are valid:
Example № 133fragment argOnRequiredArg on Dog {
doesKnowCommand(dogCommand: SIT)
}
fragment argOnOptional on Dog {
isHouseTrained(atOtherHomes: true) @include(if: true)
}
the following is invalid since command
is not defined on DogCommand
.
Counter Example № 134fragment invalidArgName on Dog {
doesKnowCommand(command: CLEAN_UP_HOUSE)
}
and this is also invalid as unless
is not defined on @include
.
Counter Example № 135fragment invalidArgName on Dog {
isHouseTrained(atOtherHomes: true) @include(unless: false)
}
In order to explore more complicated argument examples, let’s add the following to our type system:
Example № 136type Arguments {
multipleRequirements(x: Int!, y: Int!): Int!
booleanArgField(booleanArg: Boolean): Boolean
floatArgField(floatArg: Float): Float
intArgField(intArg: Int): Int
nonNullBooleanArgField(nonNullBooleanArg: Boolean!): Boolean!
booleanListArgField(booleanListArg: [Boolean]!): [Boolean]
optionalNonNullBooleanArgField(optionalBooleanArg: Boolean! = false): Boolean!
}
extend type Query {
arguments: Arguments
}
Order does not matter in arguments. Therefore both the following examples are valid.
Example № 137fragment multipleArgs on Arguments {
multipleRequirements(x: 1, y: 2)
}
fragment multipleArgsReverseOrder on Arguments {
multipleRequirements(y: 2, x: 1)
}
5.4.2Argument Uniqueness
Fields and directives treat arguments as a mapping of argument name to value. More than one argument with the same name in an argument set is ambiguous and invalid.
Formal Specification
- For each argument in the Document:
- Let argumentName be the Name of argument.
- Let arguments be all Arguments named argumentName in the Argument Set which contains argument.
- arguments must be the set containing only argument.
5.4.3Required 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 argumentDefinition in argumentDefinitions:
- Let type be the expected type of argumentDefinition.
- Let defaultValue be the default value of argumentDefinition.
- If type is Non-Null and defaultValue does not exist:
- Let argumentName be the name of argumentDefinition.
- 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. An argument is required if the argument type is non-null and does not have a default value. Otherwise, the argument is optional.
For example the following are valid:
Example № 138fragment goodBooleanArg on Arguments {
booleanArgField(booleanArg: true)
}
fragment goodNonNullArg on Arguments {
nonNullBooleanArgField(nonNullBooleanArg: true)
}
The argument can be omitted from a field with a nullable argument.
Therefore the following fragment is valid:
Example № 139fragment goodBooleanArgDefault on Arguments {
booleanArgField
}
but this is not valid on a required argument.
Counter Example № 140fragment missingRequiredArg on Arguments {
nonNullBooleanArgField
}
Providing the explicit value null is also not valid since required arguments always have a non-null type.
Counter Example № 141fragment missingRequiredArg on Arguments {
nonNullBooleanArgField(nonNullBooleanArg: null)
}
5.5Fragments
5.5.1Fragment Declarations
5.5.1.1Fragment Name Uniqueness
Formal Specification
- For each fragment definition fragment in the document:
- Let fragmentName be the name of fragment.
- Let fragments be all fragment definitions in the document named fragmentName.
- fragments must be a set of one.
Explanatory Text
Fragment definitions are referenced in fragment spreads by name. To avoid ambiguity, each fragment’s name must be unique within a document.
Inline fragments are not considered fragment definitions, and are unaffected by this validation rule.
For example the following document is valid:
Example № 142{
dog {
...fragmentOne
...fragmentTwo
}
}
fragment fragmentOne on Dog {
name
}
fragment fragmentTwo on Dog {
owner {
name
}
}
While this document is invalid:
Counter Example № 143{
dog {
...fragmentOne
}
}
fragment fragmentOne on Dog {
name
}
fragment fragmentOne on Dog {
owner {
name
}
}
5.5.1.2Fragment Spread Type Existence
Formal Specification
- For each named spread namedSpread in the document:
- Let fragment be the target of namedSpread.
- The target type of fragment must be defined in the schema.
Explanatory Text
Fragments must be specified on types that exist in the schema. This applies for both named and inline fragments. If they are not defined in the schema, the fragment is invalid.
For example the following fragments are valid:
Example № 144fragment correctType on Dog {
name
}
fragment inlineFragment on Dog {
... on Dog {
name
}
}
fragment inlineFragment2 on Dog {
... @include(if: true) {
name
}
}
and the following do not validate:
Counter Example № 145fragment notOnExistingType on NotInSchema {
name
}
fragment inlineNotExistingType on Dog {
... on NotInSchema {
name
}
}
5.5.1.3Fragments on Object, Interface or Union Types
Formal Specification
- For each fragment defined in the document:
- The target type of fragment must have kind UNION, INTERFACE, or OBJECT.
Explanatory Text
Fragments can only be declared on unions, interfaces, and objects. They are invalid on scalars. They can only be applied on non-leaf fields. This rule applies to both inline and named fragments.
The following fragment declarations are valid:
Example № 146fragment fragOnObject on Dog {
name
}
fragment fragOnInterface on Pet {
name
}
fragment fragOnUnion on CatOrDog {
... on Dog {
name
}
}
and the following are invalid:
Counter Example № 147fragment fragOnScalar on Int {
something
}
fragment inlineFragOnScalar on Dog {
... on Boolean {
somethingElse
}
}
5.5.1.4Fragments Must Be Used
Formal Specification
- For each fragment defined in the document:
- fragment must be the target of at least one spread in the document.
Explanatory Text
Defined fragments must be used within a document.
For example the following is an invalid document:
Counter Example № 148fragment nameFragment on Dog { # unused
name
}
{
dog {
name
}
}
5.5.2Fragment Spreads
Field selection is also determined by spreading fragments into one another. The selection set of the target fragment is combined into the selection set at the level at which the target fragment is referenced.
5.5.2.1Fragment Spread Target Defined
Formal Specification
- For every namedSpread in the document:
- Let fragment be the target of namedSpread.
- fragment must be defined in the document.
Explanatory Text
Named fragment spreads must refer to fragments defined within the document. It is a validation error if the target of a spread is not defined.
Counter Example № 149{
dog {
...undefinedFragment
}
}
5.5.2.2Fragment Spreads Must Not Form Cycles
Formal Specification
- For each fragmentDefinition in the document:
- Let visited be the empty set.
- DetectFragmentCycles(fragmentDefinition, visited).
- Let spreads be all fragment spread descendants of fragmentDefinition.
- For each spread in spreads:
- visited must not contain spread.
- Let nextVisited be the set including spread and members of visited.
- Let nextFragmentDefinition be the target of spread.
- DetectFragmentCycles(nextFragmentDefinition, nextVisited).
Explanatory Text
The graph of fragment spreads must not form any cycles including spreading itself. Otherwise an operation could infinitely spread or infinitely execute on cycles in the underlying data.
This invalidates fragments that would result in an infinite spread:
Counter Example № 150{
dog {
...nameFragment
}
}
fragment nameFragment on Dog {
name
...barkVolumeFragment
}
fragment barkVolumeFragment on Dog {
barkVolume
...nameFragment
}
If the above fragments were inlined, this would result in the infinitely large:
Example № 151{
dog {
name
barkVolume
name
barkVolume
name
barkVolume
name
# forever...
}
}
This also invalidates fragments that would result in an infinite recursion when executed against cyclic data:
Counter Example № 152{
dog {
...dogFragment
}
}
fragment dogFragment on Dog {
name
owner {
...ownerFragment
}
}
fragment ownerFragment on Human {
name
pets {
...dogFragment
}
}
5.5.2.3Fragment Spread Is Possible
Formal Specification
- For each spread (named or inline) defined in the document:
- Let fragment be the target of spread.
- Let fragmentType be the type condition of fragment.
- Let parentType be the type of the selection set containing spread.
- Let applicableTypes be the intersection of GetPossibleTypes(fragmentType) and GetPossibleTypes(parentType).
- applicableTypes must not be empty.
- If type is an object type, return a set containing type.
- If type is an interface type, return the set of types implementing type.
- If type is a union type, return the set of possible types of type.
Explanatory Text
Fragments are declared on a type and will only apply when the runtime object type matches the type condition. They also are spread within the context of a parent type. A fragment spread is only valid if its type condition could ever apply within the parent type.
5.5.2.3.1Object Spreads in Object Scope
In the scope of an object type, the only valid object type fragment spread is one that applies to the same type that is in scope.
For example
Example № 153fragment dogFragment on Dog {
... on Dog {
barkVolume
}
}
and the following is invalid
Counter Example № 154fragment catInDogFragmentInvalid on Dog {
... on Cat {
meowVolume
}
}
5.5.2.3.2Abstract Spreads in Object Scope
In scope of an object type, unions or interface spreads can be used if the object type implements the interface or is a member of the union.
For example
Example № 155fragment petNameFragment on Pet {
name
}
fragment interfaceWithinObjectFragment on Dog {
...petNameFragment
}
is valid because Dog implements Pet.
Likewise
Example № 156fragment catOrDogNameFragment on CatOrDog {
... on Cat {
meowVolume
}
}
fragment unionWithObjectFragment on Dog {
...catOrDogNameFragment
}
is valid because Dog is a member of the CatOrDog union. It is worth noting that if one inspected the contents of the CatOrDogNameFragment you could note that no valid results would ever be returned. However we do not specify this as invalid because we only consider the fragment declaration, not its body.
5.5.2.3.3Object Spreads in Abstract Scope
Union or interface spreads can be used within the context of an object type fragment, but only if the object type is one of the possible types of that interface or union.
For example, the following fragments are valid:
Example № 157fragment petFragment on Pet {
name
... on Dog {
barkVolume
}
}
fragment catOrDogFragment on CatOrDog {
... on Cat {
meowVolume
}
}
petFragment is valid because Dog implements the interface Pet. catOrDogFragment is valid because Cat is a member of the CatOrDog union.
By contrast the following fragments are invalid:
Counter Example № 158fragment sentientFragment on Sentient {
... on Dog {
barkVolume
}
}
fragment humanOrAlienFragment on HumanOrAlien {
... on Cat {
meowVolume
}
}
Dog does not implement the interface Sentient and therefore sentientFragment can never return meaningful results. Therefore the fragment is invalid. Likewise Cat is not a member of the union HumanOrAlien, and it can also never return meaningful results, making it invalid.
5.5.2.3.4Abstract Spreads in Abstract Scope
Union or interfaces fragments can be used within each other. As long as there exists at least one object type that exists in the intersection of the possible types of the scope and the spread, the spread is considered valid.
So for example
Example № 159fragment unionWithInterface on Pet {
...dogOrHumanFragment
}
fragment dogOrHumanFragment on DogOrHuman {
... on Dog {
barkVolume
}
}
is considered valid because Dog implements interface Pet and is a member of DogOrHuman.
However
Counter Example № 160fragment nonIntersectingInterfaces on Pet {
...sentientFragment
}
fragment sentientFragment on Sentient {
name
}
is not valid because there exists no type that implements both Pet and Sentient.
Interface Spreads in Implemented Interface Scope
Additionally, an interface type fragment can always be spread into an interface scope which it implements.
In the example below, the ...resourceFragment
fragments spreads is valid, since Resource
implements Node
.
Example № 161interface Node {
id: ID!
}
interface Resource implements Node {
id: ID!
url: String
}
fragment interfaceWithInterface on Node {
...resourceFragment
}
fragment resourceFragment on Resource {
url
}
5.6Values
5.6.1Values of Correct Type
Formal Specification
- For each input Value value in the document:
- Let type be the type expected in the position value is found.
- value must be coercible to type.
Explanatory Text
Literal values must be compatible with the type expected in the position they are found as per the coercion rules defined in the Type System chapter.
The type expected in a position includes the type defined by the argument a value is provided for, the type defined by an input object field a value is provided for, and the type of a variable definition a default value is provided for.
The following examples are valid use of value literals:
Example № 162fragment goodBooleanArg on Arguments {
booleanArgField(booleanArg: true)
}
fragment coercedIntIntoFloatArg on Arguments {
# Note: The input coercion rules for Float allow Int literals.
floatArgField(floatArg: 123)
}
query goodComplexDefaultValue($search: FindDogInput = { name: "Fido" }) {
findDog(searchBy: $search) {
name
}
}
Non-coercible values (such as a String into an Int) are invalid. The following examples are invalid:
Counter Example № 163fragment stringIntoInt on Arguments {
intArgField(intArg: "123")
}
query badComplexValue {
findDog(searchBy: { name: 123 }) {
name
}
}
5.6.2Input Object Field Names
Formal Specification
- For each Input Object Field inputField in the document:
- Let inputFieldName be the Name of inputField.
- Let inputFieldDefinition be the input field definition provided by the parent input object type named inputFieldName.
- inputFieldDefinition must exist.
Explanatory Text
Every input field provided in an input object value must be defined in the set of possible fields of that input object’s expected type.
For example the following example input object is valid:
Example № 164{
findDog(searchBy: { name: "Fido" }) {
name
}
}
While the following example input-object uses a field “favoriteCookieFlavor” which is not defined on the expected type:
Counter Example № 165{
findDog(searchBy: { favoriteCookieFlavor: "Bacon" }) {
name
}
}
5.6.3Input Object Field Uniqueness
Formal Specification
- For each input object value inputObject in the document:
- For every inputField in inputObject:
- Let name be the Name of inputField.
- Let fields be all Input Object Fields named name in inputObject.
- fields must be the set containing only inputField.
- For every inputField in inputObject:
Explanatory Text
Input objects must not contain more than one field of the same name, otherwise an ambiguity would exist which includes an ignored portion of syntax.
For example the following document will not pass validation.
Counter Example № 166{
field(arg: { field: true, field: false })
}
5.6.4Input Object Required Fields
Formal Specification
- For each Input Object in the document:
- Let fields be the fields provided by that Input Object.
- Let fieldDefinitions be the set of input field definitions of that Input Object.
- For each fieldDefinition in fieldDefinitions:
- Let type be the expected type of fieldDefinition.
- Let defaultValue be the default value of fieldDefinition.
- If type is Non-Null and defaultValue does not exist:
- Let fieldName be the name of fieldDefinition.
- Let field be the input field in fields named fieldName.
- field must exist.
- Let value be the value of field.
- value must not be the null literal.
Explanatory Text
Input object fields may be required. Much like a field may have required arguments, an input object may have required fields. An input field is required if it has a non-null type and does not have a default value. Otherwise, the input object field is optional.
5.7Directives
5.7.1Directives Are Defined
Formal Specification
- For every directive in a document:
- Let directiveName be the name of directive.
- Let directiveDefinition be the directive named directiveName.
- directiveDefinition must exist.
Explanatory Text
GraphQL services define what directives they support. For each usage of a directive, the directive must be available on that service.
5.7.2Directives Are in Valid Locations
Formal Specification
- For every directive in a document:
- Let directiveName be the name of directive.
- Let directiveDefinition be the directive named directiveName.
- Let locations be the valid locations for directiveDefinition.
- Let adjacent be the AST node the directive affects.
- adjacent must be represented by an item within locations.
Explanatory Text
GraphQL services define what directives they support and where they support them. For each usage of a directive, the directive must be used in a location that the service has declared support for.
For example the following document will not pass validation because @skip
does not provide QUERY
as a valid location.
Counter Example № 167query @skip(if: $foo) {
field
}
5.7.3Directives Are Unique per Location
Formal Specification
- For every location in the document for which Directives can apply:
- Let directives be the set of Directives which apply to location and are not repeatable.
- For each directive in directives:
- Let directiveName be the name of directive.
- Let namedDirectives be the set of all Directives named directiveName in directives.
- namedDirectives must be a set of one.
Explanatory Text
GraphQL allows directives that are defined as repeatable
to be used more than once on the definition they apply to, possibly with different arguments. In contrast, if a directive is not repeatable
, then only one occurrence of it is allowed per location.
For example, the following document will not pass validation because non-repeatable @skip
has been used twice for the same field:
Counter Example № 168query ($foo: Boolean = true, $bar: Boolean = false) {
field @skip(if: $foo) @skip(if: $bar)
}
However the following example is valid because @skip
has been used only once per location, despite being used twice in the operation and on the same named field:
Example № 169query ($foo: Boolean = true, $bar: Boolean = false) {
field @skip(if: $foo) {
subfieldA
}
field @skip(if: $bar) {
subfieldB
}
}
5.7.4Defer And Stream Directives Are Used On Valid Root Field
Formal Specification
- For every directive in a document.
- Let directiveName be the name of directive.
- Let mutationType be the root Mutation type in schema.
- Let subscriptionType be the root Subscription type in schema.
- If directiveName is “defer” or “stream”:
- The parent type of directive must not be mutationType or subscriptionType.
Explanatory Text
The defer and stream directives are not allowed to be used on root fields of the mutation or subscription type.
For example, the following document will not pass validation because @defer
has been used on a root mutation field:
Counter Example № 170mutation {
... @defer {
mutationField
}
}
5.7.5Defer And Stream Directives Are Used On Valid Operations
Formal Specification
- Let subscriptionFragments be the empty set.
- For each operation in a document:
- If operation is a subscription operation:
- Let fragments be every fragment referenced by that operation transitively.
- For each fragment in fragments:
- Let fragmentName be the name of fragment.
- Add fragmentName to subscriptionFragments.
- If operation is a subscription operation:
- For every directive in a document:
- If directiveName is not “defer” or “stream”:
- Continue to the next directive.
- Let ancestor be the ancestor operation or fragment definition of directive.
- If ancestor is a fragment definition:
- If the fragment name of ancestor is not present in subscriptionFragments:
- Continue to the next directive.
- If the fragment name of ancestor is not present in subscriptionFragments:
- If ancestor is not a subscription operation:
- Continue to the next directive.
- Let if be the argument named “if” on directive.
- if must be defined.
- Let argumentValue be the value passed to if.
- argumentValue must be a variable, or the boolean value “false”.
- If directiveName is not “defer” or “stream”:
Explanatory Text
The defer and stream directives can not be used to defer or stream data in subscription operations. If these directives appear in a subscription operation they must be disabled using the “if” argument. This rule will not permit any defer or stream directives on a subscription operation that cannot be disabled using the “if” argument.
For example, the following document will not pass validation because @defer
has been used in a subscription operation with no “if” argument defined:
Counter Example № 171subscription sub {
newMessage {
... @defer {
body
}
}
}
5.7.6Defer And Stream Directive Labels Are Unique
Formal Specification
- Let labelValues be an empty set.
- For every directive in the document:
- Let directiveName be the name of directive.
- If directiveName is “defer” or “stream”:
- For every argument in directive:
- Let argumentName be the name of argument.
- Let argumentValue be the value passed to argument.
- If argumentName is “label”:
- argumentValue must not be a variable.
- argumentValue must not be present in labelValues.
- Append argumentValue to labelValues.
- For every argument in directive:
Explanatory Text
The @defer
and @stream
directives each accept an argument “label”. This label may be used by GraphQL clients to uniquely identify response payloads. If a label is passed, it must not be a variable and it must be unique within all other @defer
and @stream
directives in the document.
For example the following document is valid:
Example № 172{
dog {
...fragmentOne
...fragmentTwo @defer(label: "dogDefer")
}
pets @stream(label: "petStream") {
name
}
}
fragment fragmentOne on Dog {
name
}
fragment fragmentTwo on Dog {
owner {
name
}
}
For example, the following document will not pass validation because the same label is used in different @defer
and @stream
directives.:
Counter Example № 173{
dog {
...fragmentOne @defer(label: "MyLabel")
}
pets @stream(label: "MyLabel") {
name
}
}
fragment fragmentOne on Dog {
name
}
5.7.7Stream Directives Are Used On List Fields
Formal Specification
- For every directive in a document.
- Let directiveName be the name of directive.
- If directiveName is “stream”:
- Let adjacent be the AST node the directive affects.
- adjacent must be a List type.
Explanatory Text
GraphQL directive locations do not provide enough granularity to distinguish the type of fields used in a GraphQL document. Since the stream directive is only valid on list fields, an additional validation rule must be used to ensure it is used correctly.
For example, the following document will only pass validation if field
is defined as a List type in the associated schema.
Counter Example № 174query {
field @stream(initialCount: 0)
}
5.8Variables
5.8.1Variable Uniqueness
Formal Specification
- For every operation in the document:
- For every variable defined on operation:
- Let variableName be the name of variable.
- Let variables be the set of all variables named variableName on operation.
- variables must be a set of one.
- For every variable defined on operation:
Explanatory Text
If any operation defines more than one variable with the same name, it is ambiguous and invalid. It is invalid even if the type of the duplicate variable is the same.
Counter Example № 175query houseTrainedQuery($atOtherHomes: Boolean, $atOtherHomes: Boolean) {
dog {
isHouseTrained(atOtherHomes: $atOtherHomes)
}
}
It is valid for multiple operations to define a variable with the same name. If two operations reference the same fragment, it might actually be necessary:
Example № 176query A($atOtherHomes: Boolean) {
...HouseTrainedFragment
}
query B($atOtherHomes: Boolean) {
...HouseTrainedFragment
}
fragment HouseTrainedFragment on Query {
dog {
isHouseTrained(atOtherHomes: $atOtherHomes)
}
}
5.8.2Variables Are Input Types
Formal Specification
- For every operation in a document:
- For every variable on each operation:
- Let variableType be the type of variable.
- IsInputType(variableType) must be true.
- For every variable on each operation:
Explanatory Text
Variables can only be input types. Objects, unions, and interfaces cannot be used as inputs.
For these examples, consider the following type system additions:
Example № 177extend type Query {
booleanList(booleanListArg: [Boolean!]): Boolean
}
The following operations are valid:
Example № 178query takesBoolean($atOtherHomes: Boolean) {
dog {
isHouseTrained(atOtherHomes: $atOtherHomes)
}
}
query takesComplexInput($search: FindDogInput) {
findDog(searchBy: $search) {
name
}
}
query TakesListOfBooleanBang($booleans: [Boolean!]) {
booleanList(booleanListArg: $booleans)
}
The following operations are invalid:
Counter Example № 179query takesCat($cat: Cat) {
# ...
}
query takesDogBang($dog: Dog!) {
# ...
}
query takesListOfPet($pets: [Pet]) {
# ...
}
query takesCatOrDog($catOrDog: CatOrDog) {
# ...
}
5.8.3All Variable Uses Defined
Formal Specification
- For each operation in a document:
- For each variableUsage in scope, variable must be in operation‘s variable list.
- Let fragments be every fragment referenced by that operation transitively.
- For each fragment in fragments:
- For each variableUsage in scope of fragment, variable must be in operation‘s variable list.
Explanatory Text
Variables are scoped on a per-operation basis. That means that any variable used within the context of an operation must be defined at the top level of that operation
For example:
Example № 180query variableIsDefined($atOtherHomes: Boolean) {
dog {
isHouseTrained(atOtherHomes: $atOtherHomes)
}
}
is valid. $atOtherHomes is defined by the operation.
By contrast the following document is invalid:
Counter Example № 181query variableIsNotDefined {
dog {
isHouseTrained(atOtherHomes: $atOtherHomes)
}
}
$atOtherHomes is not defined by the operation.
Fragments complicate this rule. Any fragment transitively included by an operation has access to the variables defined by that operation. Fragments can appear within multiple operations and therefore variable usages must correspond to variable definitions in all of those operations.
For example the following is valid:
Example № 182query variableIsDefinedUsedInSingleFragment($atOtherHomes: Boolean) {
dog {
...isHouseTrainedFragment
}
}
fragment isHouseTrainedFragment on Dog {
isHouseTrained(atOtherHomes: $atOtherHomes)
}
since isHouseTrainedFragment is used within the context of the operation variableIsDefinedUsedInSingleFragment and the variable is defined by that operation.
On the other hand, if a fragment is included within an operation that does not define a referenced variable, the document is invalid.
Counter Example № 183query variableIsNotDefinedUsedInSingleFragment {
dog {
...isHouseTrainedFragment
}
}
fragment isHouseTrainedFragment on Dog {
isHouseTrained(atOtherHomes: $atOtherHomes)
}
This applies transitively as well, so the following also fails:
Counter Example № 184query variableIsNotDefinedUsedInNestedFragment {
dog {
...outerHouseTrainedFragment
}
}
fragment outerHouseTrainedFragment on Dog {
...isHouseTrainedFragment
}
fragment isHouseTrainedFragment on Dog {
isHouseTrained(atOtherHomes: $atOtherHomes)
}
Variables must be defined in all operations in which a fragment is used.
Example № 185query houseTrainedQueryOne($atOtherHomes: Boolean) {
dog {
...isHouseTrainedFragment
}
}
query houseTrainedQueryTwo($atOtherHomes: Boolean) {
dog {
...isHouseTrainedFragment
}
}
fragment isHouseTrainedFragment on Dog {
isHouseTrained(atOtherHomes: $atOtherHomes)
}
However the following does not validate:
Counter Example № 186query houseTrainedQueryOne($atOtherHomes: Boolean) {
dog {
...isHouseTrainedFragment
}
}
query houseTrainedQueryTwoNotDefined {
dog {
...isHouseTrainedFragment
}
}
fragment isHouseTrainedFragment on Dog {
isHouseTrained(atOtherHomes: $atOtherHomes)
}
This is because houseTrainedQueryTwoNotDefined does not define a variable $atOtherHomes but that variable is used by isHouseTrainedFragment which is included in that operation.
5.8.4All Variables Used
Formal Specification
- For every operation in the document:
- Let variables be the variables defined by that operation.
- Each variable in variables must be used at least once in either the operation scope itself or any fragment transitively referenced by that operation.
Explanatory Text
All variables defined by an operation must be used in that operation or a fragment transitively included by that operation. Unused variables cause a validation error.
For example the following is invalid:
Counter Example № 187query variableUnused($atOtherHomes: Boolean) {
dog {
isHouseTrained
}
}
because $atOtherHomes is not referenced.
These rules apply to transitive fragment spreads as well:
Example № 188query variableUsedInFragment($atOtherHomes: Boolean) {
dog {
...isHouseTrainedFragment
}
}
fragment isHouseTrainedFragment on Dog {
isHouseTrained(atOtherHomes: $atOtherHomes)
}
The above is valid since $atOtherHomes is used in isHouseTrainedFragment which is included by variableUsedInFragment.
If that fragment did not have a reference to $atOtherHomes it would be not valid:
Counter Example № 189query variableNotUsedWithinFragment($atOtherHomes: Boolean) {
dog {
...isHouseTrainedWithoutVariableFragment
}
}
fragment isHouseTrainedWithoutVariableFragment on Dog {
isHouseTrained
}
All operations in a document must use all of their variables.
As a result, the following document does not validate.
Counter Example № 190query queryWithUsedVar($atOtherHomes: Boolean) {
dog {
...isHouseTrainedFragment
}
}
query queryWithExtraVar($atOtherHomes: Boolean, $extra: Int) {
dog {
...isHouseTrainedFragment
}
}
fragment isHouseTrainedFragment on Dog {
isHouseTrained(atOtherHomes: $atOtherHomes)
}
This document is not valid because queryWithExtraVar defines an extraneous variable.
5.8.5All Variable Usages Are Allowed
Formal Specification
- For each operation in document:
- Let variableUsages be all usages transitively included in the operation.
- For each variableUsage in variableUsages:
- Let variableName be the name of variableUsage.
- Let variableDefinition be the VariableDefinition named variableName defined within operation.
- IsVariableUsageAllowed(variableDefinition, variableUsage) must be true.
- Let variableType be the expected type of variableDefinition.
- Let locationType be the expected type of the Argument, ObjectField, or ListValue entry where variableUsage is located.
- If locationType is a non-null type AND variableType is NOT a non-null type:
- Let hasNonNullVariableDefaultValue be true if a default value exists for variableDefinition and is not the value null.
- Let hasLocationDefaultValue be true if a default value exists for the Argument or ObjectField where variableUsage is located.
- If hasNonNullVariableDefaultValue is NOT true AND hasLocationDefaultValue is NOT true, return false.
- Let nullableLocationType be the unwrapped nullable type of locationType.
- Return AreTypesCompatible(variableType, nullableLocationType).
- Return AreTypesCompatible(variableType, locationType).
- If locationType is a non-null type:
- If variableType is NOT a non-null type, return false.
- Let nullableLocationType be the unwrapped nullable type of locationType.
- Let nullableVariableType be the unwrapped nullable type of variableType.
- Return AreTypesCompatible(nullableVariableType, nullableLocationType).
- Otherwise, if variableType is a non-null type:
- Let nullableVariableType be the nullable type of variableType.
- Return AreTypesCompatible(nullableVariableType, locationType).
- Otherwise, if locationType is a list type:
- If variableType is NOT a list type, return false.
- Let itemLocationType be the unwrapped item type of locationType.
- Let itemVariableType be the unwrapped item type of variableType.
- Return AreTypesCompatible(itemVariableType, itemLocationType).
- Otherwise, if variableType is a list type, return false.
- Return true if variableType and locationType are identical, otherwise false.
Explanatory Text
Variable usages must be compatible with the arguments they are passed to.
Validation failures occur when variables are used in the context of types that are complete mismatches, or if a nullable type in a variable is passed to a non-null argument type.
Types must match:
Counter Example № 191query intCannotGoIntoBoolean($intArg: Int) {
arguments {
booleanArgField(booleanArg: $intArg)
}
}
$intArg typed as Int cannot be used as an argument to booleanArg, typed as Boolean.
List cardinality must also be the same. For example, lists cannot be passed into singular values.
Counter Example № 192query booleanListCannotGoIntoBoolean($booleanListArg: [Boolean]) {
arguments {
booleanArgField(booleanArg: $booleanListArg)
}
}
Nullability must also be respected. In general a nullable variable cannot be passed to a non-null argument.
Counter Example № 193query booleanArgQuery($booleanArg: Boolean) {
arguments {
nonNullBooleanArgField(nonNullBooleanArg: $booleanArg)
}
}
For list types, the same rules around nullability apply to both outer types and inner types. A nullable list cannot be passed to a non-null list, and a list of nullable values cannot be passed to a list of non-null values. The following is valid:
Example № 194query nonNullListToList($nonNullBooleanList: [Boolean]!) {
arguments {
booleanListArgField(booleanListArg: $nonNullBooleanList)
}
}
However, a nullable list cannot be passed to a non-null list:
Counter Example № 195query listToNonNullList($booleanList: [Boolean]) {
arguments {
nonNullBooleanListField(nonNullBooleanListArg: $booleanList)
}
}
This would fail validation because a [T]
cannot be passed to a [T]!
. Similarly a [T]
cannot be passed to a [T!]
.
Allowing Optional Variables When Default Values Exist
A notable exception to typical variable type compatibility is allowing a variable definition with a nullable type to be provided to a non-null location as long as either that variable or that location provides a default value.
In the example below, an optional variable $booleanArg
is allowed to be used in the non-null argument optionalBooleanArg
because the field argument is optional since it provides a default value in the schema.
Example № 196query booleanArgQueryWithDefault($booleanArg: Boolean) {
arguments {
optionalNonNullBooleanArgField(optionalBooleanArg: $booleanArg)
}
}
In the example below, an optional variable $booleanArg
is allowed to be used in the non-null argument (nonNullBooleanArg
) because the variable provides a default value in the operation. This behavior is explicitly supported for compatibility with earlier editions of this specification. GraphQL authoring tools may wish to report this as a warning with the suggestion to replace Boolean
with Boolean!
to avoid ambiguity.
Example № 197query booleanArgQueryWithDefault($booleanArg: Boolean = true) {
arguments {
nonNullBooleanArgField(nonNullBooleanArg: $booleanArg)
}
}
6Execution
A GraphQL service generates a response from a request via execution.
A request for execution consists of a few pieces of information:
- schema: The schema to use, typically solely provided by the GraphQL service.
- document: A Document which must contain GraphQL OperationDefinition and may contain FragmentDefinition.
- operationName (optional): The name of the Operation in the Document to execute.
- variableValues (optional): Values for any Variables defined by the Operation.
- initialValue (optional): An initial value corresponding to the root type 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.
Given this information, the result of ExecuteRequest(schema, document, operationName, variableValues, initialValue) produces the response, to be formatted according to the Response section below.
6.1Executing Requests
To execute a request, the executor must have a parsed Document and a selected operation name to run if the document defines multiple operations, otherwise the document is expected to only contain a single operation. The result of the request is determined by the result of executing this operation according to the “Executing Operations” section below.
- Let operation be the result of GetOperation(document, operationName).
- Let coercedVariableValues be the result of CoerceVariableValues(schema, operation, variableValues).
- If operation is a query operation:
- Return ExecuteQuery(operation, schema, coercedVariableValues, initialValue).
- Otherwise if operation is a mutation operation:
- Return ExecuteMutation(operation, schema, coercedVariableValues, initialValue).
- Otherwise if operation is a subscription operation:
- Return Subscribe(operation, schema, coercedVariableValues, initialValue).
- If operationName is null:
- If document contains exactly one operation.
- Return the Operation contained in the document.
- Otherwise raise a request error requiring operationName.
- If document contains exactly one operation.
- Otherwise:
- Let operation be the Operation named operationName in document.
- If operation was not found, raise a request error.
- Return operation.
6.1.1Validating Requests
As explained in the Validation section, only requests which pass all validation rules should be executed. If validation errors are known, they should be reported in the list of “errors” in the response and the request must fail without execution.
Typically validation is performed in the context of a request immediately before execution, however a GraphQL service may execute a request without immediately validating it if that exact same request is known to have been validated before. A GraphQL service should only execute requests which at some point were known to be free of any validation errors, and have since not changed.
For example: the request may be validated during development, provided it does not later change, or a service may validate a request once and memoize the result to avoid validating the same request again in the future.
6.1.2Coercing Variable Values
If the operation has defined any variables, then the values for those variables need to be coerced using the input coercion rules of variable’s declared type. If a request error is encountered during input coercion of variable values, then the operation fails without execution.
- Let coercedValues be an empty unordered Map.
- Let variablesDefinition be the variables defined by operation.
- For each variableDefinition in variablesDefinition:
- Let variableName be the name of variableDefinition.
- Let variableType be the expected type of variableDefinition.
- Assert: IsInputType(variableType) must be true.
- Let defaultValue be the default value for variableDefinition.
- Let hasValue be true if variableValues provides a value for the name variableName.
- Let value be the value provided in variableValues for the name variableName.
- If hasValue is not true and defaultValue exists (including null):
- Add an entry to coercedValues named variableName with the value defaultValue.
- Otherwise if variableType is a Non-Nullable type, and either hasValue is not true or value is null, raise a request error.
- Otherwise if hasValue is true:
- If value is null:
- Add an entry to coercedValues named variableName with the value null.
- Otherwise:
- If value cannot be coerced according to the input coercion rules of variableType, raise a request error.
- Let coercedValue be the result of coercing value according to the input coercion rules of variableType.
- Add an entry to coercedValues named variableName with the value coercedValue.
- If value is null:
- Return coercedValues.
6.2Executing Operations
The type system, as described in the “Type System” section of the spec, must provide a query root operation type. If mutations or subscriptions are supported, it must also provide a mutation or subscription root operation type, respectively.
6.2.1Query
If the operation is a query, the result of the operation is the result of executing the operation’s top level selection set with the query root operation type.
An initial value may be provided when executing a query operation.
- Let queryType be the root Query type in schema.
- Assert: queryType is an Object type.
- Let selectionSet be the top level selection set in query.
- Return ExecuteRootSelectionSet(variableValues, initialValue, queryType, selectionSet).
6.2.2Mutation
If the operation is a mutation, the result of the operation is the result of executing the operation’s top level selection set on the mutation root object type. This selection set should be executed serially.
It is expected that the top level fields in a mutation operation perform side-effects on the underlying data system. Serial execution of the provided mutations ensures against race conditions during these side-effects.
- Let mutationType be the root Mutation type in schema.
- Assert: mutationType is an Object type.
- Let selectionSet be the top level selection set in mutation.
- Return ExecuteRootSelectionSet(variableValues, initialValue, mutationType, selectionSet, true).
6.2.3Subscription
If the operation is a subscription, the result is an event stream called the “Response Stream” where each event in the event stream is the result of executing the operation for each new event on an underlying “Source Stream”.
Executing a subscription operation creates a persistent function on the service that maps an underlying Source Stream to a returned Response Stream.
- Let sourceStream be the result of running CreateSourceEventStream(subscription, schema, variableValues, initialValue).
- Let responseStream be the result of running MapSourceToResponseEvent(sourceStream, subscription, schema, variableValues).
- Return responseStream.
As an example, consider a chat application. To subscribe to new messages posted to the chat room, the client sends a request like so:
Example № 198subscription NewMessages {
newMessage(roomId: 123) {
sender
text
}
}
While the client is subscribed, whenever new messages are posted to chat room with ID “123”, the selection for “sender” and “text” will be evaluated and published to the client, for example:
Example № 199{
"data": {
"newMessage": {
"sender": "Hagrid",
"text": "You're a wizard!"
}
}
}
The “new message posted to chat room” could use a “Pub-Sub” system where the chat room ID is the “topic” and each “publish” contains the sender and text.
Event Streams
An event stream represents a sequence of discrete events over time which can be observed. As an example, a “Pub-Sub” system may produce an event stream when “subscribing to a topic”, with an event occurring on that event stream for each “publish” to that topic. Event streams may produce an infinite sequence of events or may complete at any point. Event streams may complete in response to an error or simply because no more events will occur. An observer may at any point decide to stop observing an event stream by cancelling it, after which it must receive no more events from that event stream.
Supporting Subscriptions at Scale
Query and mutation operations are stateless, allowing scaling via cloning of GraphQL service instances. Subscriptions, by contrast, are stateful and require maintaining the GraphQL document, variables, and other context over the lifetime of the subscription.
Consider the behavior of your system when state is lost due to the failure of a single machine in a service. Durability and availability may be improved by having separate dedicated services for managing subscription state and client connectivity.
Delivery Agnostic
GraphQL subscriptions do not require any specific serialization format or transport mechanism. GraphQL specifies algorithms for the creation of the response stream, the content of each payload on that stream, and the closing of that stream. There are intentionally no specifications for message acknowledgement, buffering, resend requests, or any other quality of service (QoS) details. Message serialization, transport mechanisms, and quality of service details should be chosen by the implementing service.
6.2.3.1Source Stream
A Source Stream represents the sequence of events, each of which will trigger a GraphQL execution corresponding to that event. Like field value resolution, the logic to create a Source Stream is application-specific.
- Let subscriptionType be the root Subscription type in schema.
- Assert: subscriptionType is an Object type.
- Let selectionSet be the top level selection set in subscription.
- Let groupedFieldSet be the result of CollectFields(subscriptionType, selectionSet, variableValues).
- If groupedFieldSet does not have exactly one entry, raise a request error.
- Let fieldDetailsList be the value of the first entry in groupedFieldSet.
- Let fieldDetails be the first entry in fieldDetailsList.
- Let field be the corresponding entry on fieldDetails.
- Let fieldName be the name of field. Note: This value is unaffected if an alias is used.
- Let argumentValues be the result of CoerceArgumentValues(subscriptionType, field, variableValues).
- Let fieldStream be the result of running ResolveFieldEventStream(subscriptionType, initialValue, fieldName, argumentValues).
- Return fieldStream.
- Let resolver be the internal function provided by subscriptionType for determining the resolved event stream of a subscription field named fieldName.
- Return the result of calling resolver, providing rootValue and argumentValues.
6.2.3.2Response Stream
Each event in the underlying Source Stream triggers execution of the subscription selection set using that event as a root value.
- Return a new event stream responseStream which yields events as follows:
- For each event on sourceStream:
- Let response be the result of running ExecuteSubscriptionEvent(subscription, schema, variableValues, event).
- Yield an event containing response.
- When sourceStream completes: complete responseStream.
- For each event on sourceStream:
- Let subscriptionType be the root Subscription type in schema.
- Assert: subscriptionType is an Object type.
- Let selectionSet be the top level selection set in subscription.
- Return ExecuteRootSelectionSet(variableValues, initialValue, subscriptionType, selectionSet).
6.2.3.3Unsubscribe
Unsubscribe cancels the Response Stream when a client no longer wishes to receive payloads for a subscription. This may in turn also cancel the Source Stream. This is also a good opportunity to clean up any other resources used by the subscription.
- Cancel responseStream.
6.3Executing the Root Selection Set
To execute the root selection set, the object value being evaluated and the object type need to be known, as well as whether it must be executed serially, or may be executed in parallel.
Executing the root selection set works similarly for queries (parallel), mutations (serial), and subscriptions (where it is executed for each event in the underlying Source Stream).
First, the selection set is turned into a grouped field set; then, we execute this grouped field set and return the resulting data and errors.
- If serial is not provided, initialize it to false.
- Let groupedFieldSet and newDeferUsages be the result of CollectFields(objectType, selectionSet, variableValues).
- Let executionPlan be the result of BuildExecutionPlan(groupedFieldSet).
- Let data and incrementalDataRecords be the result of ExecuteExecutionPlan(newDeferUsages, executionPlan, objectType, initialValue, variableValues, serial).
- Let errors be the list of all field error raised while executing the execution plan.
- If incrementalDataRecords is empty, return an unordered map containing data and errors.
- Let incrementalResults be the result of YieldIncrementalResults(data, errors, incrementalDataRecords).
- Wait for the first result in incrementalResults to be available.
- Let initialResult be that result.
- Return initialResult and BatchIncrementalResults(incrementalResults).
6.3.1Yielding Incremental Results
The procedure for yielding incremental results is specified by the YieldIncrementalResults() algorithm. The incremental state is stored within a graph, with root nodes representing the currently pending delivery groups.
For example, given the following operation:
Example № 200{
...SlowFragment @defer
fastField
}
fragment SlowFragment on Query {
...SlowestFragment @defer
slowField
}
fragment SlowestFragment on Query {
slowestField
}
A valid GraphQL executor deferring SlowFragment
must include a pending
entry to that effect within the initial result, while the pending
entry for SlowestFragment
should be delivered together with SlowFragment
.
Delivery group nodes may have three different types of child nodes:
- Child Delivery Group nodes, i.e. the node representing
SlowFragment
should have a child node representingSlowestFragment
. - Pending Incremental Data nodes, i.e. the node for
SlowFragment
should initially have a node forslowField
. - Completed Incremental Data nodes, i.e. when
slowField
is completed, the pending incremental data node forslowField
should be replaced with a node representing the completed data.
The YieldIncrementalResults() algorithm is responsible for updating the graph as it yields the incremental results. When a delivery group contains only completed incremental data nodes, the group is removed from the graph as it is delivered.
- Let graph be the result of GraphFromRecords(incrementalDataRecords).
- Let rootNodes be the result of GetNewRootNodes(graph).
- Update graph to the subgraph rooted at nodes in rootNodes.
- Yield the result of GetInitialResult(data, errors, rootNodes).
- For each completed child Pending Incremental Data node of a root node in graph:
- Let incrementalDataRecord be the Pending Incremental Data for that node; let result be the corresponding completed result.
- If data on result is null:
- Let parents be the parent nodes of executionGroup.
- Initialize completed to an empty list.
- For each node of parents:
- Append GetCompletedEntry(parent, errors) to completed.
- Remove node and all of its descendant nodes from graph, except for any descendant Incremental Data Record nodes with other parents.
- Yield the result of GetSubsequentResult(graph, completed).
- Continue to the next completed Pending Incremental Data node.
- Replace node in graph with a new node corresponding to the Completed Incremental Data for result.
- Let resultIncrementalDataRecords be incrementalDataRecords on result.
- Update graph to GraphFromRecords(resultIncrementalDataRecords, graph).
- Let completedDeferredFragments be the set of root nodes in graph without any child Pending Incremental Data nodes.
- Let completedIncrementalDataNodes be the set of completed Incremental Data nodes that are children of completedDeferredFragments.
- If completedIncrementalDataNodes is empty, continue to the next completed Pending Incremental Data Node.
- Initialize incremental to an empty list.
- For each completedIncrementalDataNode of completedIncrementalDataNodes:
- Let incrementalDataRecord be the corresponding record for completedIncrementalDataNode.
- Append GetIncrementalEntry(incrementalDataRecord, graph) to incremental.
- Remove node from graph.
- Initialize completed to an empty list.
- For each completedDeferredFragment of completedDeferredFragments:
- Append GetCompletedEntry(completedDeferredFragment) to completed.
- Remove completedDeferredFragment from graph, promoting its child Deferred Fragment nodes to root nodes.
- Let newRootNodes be the result of GetNewRootNodes(graph, rootNodes).
- Add all nodes in newRootNodes to rootNodes.
- Update graph to the subgraph rooted at nodes in rootNodes.
- Let pending be the result of GetPendingEntry(newRootNodes).
- Yield the result of GetSubsequentResult(graph, incremental, completed, pending).
- Complete this incremental result stream.
New Incremental Data Records are added to the graph by the GraphFromRecords() algorithm as Pending Incremental Data nodes directed from the Deferred Fragments they incrementally complete.
- If graph is not provided, initialize to an empty graph.
- Let newGraph be a new directed acyclic graph containing all of the nodes and edges in graph.
- For each incrementalDataRecord of incrementalDataRecords:
- Let deferredFragments be the Deferred Fragments incrementally completed by incrementalDataRecord.
- For each deferredFragment of deferredFragments:
- Reset newGraph to the result of GraphWithDeferredFragmentRecord(deferredFragment, newGraph).
- Add incrementalDataRecord to newGraph as a new Pending Incremental Data node directed from the deferredFragments that it completes.
- Return newGraph.
The GraphWithDeferredFragmentRecord() algorithm returns a new graph containing the provided Deferred Fragment Record, recursively adding its parent Deferred Fragment nodes.
- If graph contains a Deferred Fragment node representing deferredFragment, return graph.
- Let parent be the parent Deferred Fragment of deferredFragment.
- If parent is defined, let newGraph be the result of GraphWithDeferredFragmentRecord(parent, graph); otherwise, let newGraph be a new directed acyclic graph containing all of the nodes and edges in graph.
- Add deferredFragment to newGraph as a new Deferred Fragment node directed from parent, if defined.
- Return newGraph.
The GetNewRootNodes() algorithm is responsible for determining the new root nodes that must be reported as pending. Any delivery groups without any execution groups should not be reported as pending, and any child delivery groups for these “empty” delivery groups should be reported as pending in their stead.
- Initialize newRootNodes to the empty set.
- Initialize rootNodes to the set of root nodes in graph.
- For each rootNode of rootNodes:
- If rootNode has no children Pending Incremental Data nodes:
- Let children be the set of child Deferred Fragment nodes of rootNode.
- Add each of the nodes in children to rootNodes.
- Continue to the next rootNode of rootNodes.
- If oldRootNodes does not contain rootNode, add rootNode to newRootNodes.
- If rootNode has no children Pending Incremental Data nodes:
- Return newRootNodes.
Formatting of the initial result is defined by the GetInitialResult() algorithm. It will only be called when there is an incremental result stream, and so hasNext
will always be set to true.
- Let pending be the result of GetPendingEntry(pendingResults).
- Let hasNext be true.
- Return an unordered map containing data, errors, pending, and hasNext.
Formatting the pending
entries of initial and subsequentResults is defined by the GetPendingEntry() algorithm. Given a set of new root nodes added to the graph, GetPendingEntry() returns a list of formatted pending
entries.
- Initialize pending to an empty list.
- For each newRootNode of newRootNodes:
- Let id be a unique identifier for newRootNode.
- Let path and label be the corresponding entries on newRootNode.
- Let pendingEntry be an unordered map containing id, path, and label.
- Append pendingEntry to pending.
- Return pending.
Formatting of subsequent incremental results is defined by the GetSubsequentResult() algorithm. Given the current graph, and any completed
, incremental
, and pending
entries, it produces an appropriately formatted subsequent incremental response.
- Let hasNext be false if graph is empty, otherwise, true.
- Let incrementalResult be an unordered map containing hasNext.
- If incremental is provided and not empty:
- Set the corresponding entry on incrementalResult to incremental.
- If completed is not empty:
- Set the corresponding entry on incrementalResult to completed.
- If pending is provided and not empty:
- Set the corresponding entry on incrementalResult to pending.
- Return incrementalResult.
Formatting of subsequent incremental results is defined by the GetSubsequentResult() algorithm. Execution groups are tagged with the id
and subPath
combination optimized to produce the shortest subPath
.
- Let deferredFragments be the Deferred Fragments incrementally completed by incrementalDataRecord at path.
- Let result be the result of incrementalDataRecord.
- Let data and errors be the corresponding entries on result.
- Let releasedDeferredFragments be the members of deferredFragments that are root nodes in graph.
- Let bestDeferredFragment be the member of releasedDeferredFragments with the shortest path entry.
- Let subPath be the portion of path not contained by the path entry of bestDeferredFragment.
- Let id be the unique identifier for bestDeferredFragment.
- Return an unordered map containing id, subPath, data, and errors.
Formatting of completed incremental results is defined by the GetCompletedEntry() algorithm.
- Let id be the unique identifier for pendingResult.
- Let completedEntry be an unordered map containing id.
- If errors is not empty, set the corresponding entry on completedEntry to errors.
- Return completedEntry.
6.3.2Batching Incremental Results
- Return a new stream batchedIncrementalResults which yields events as follows:
- While incrementalResults is not closed:
- Let availableIncrementalResults be a list of one or more Incremental Results available on incrementalResults.
- Let batchedIncrementalResult be an unordered map created by merging the items in availableIncrementalResults into a single unordered map, concatenating list entries as necessary, and setting hasNext to the value of hasNext on the final item in the list.
- Yield batchedIncrementalResult.
6.4Executing an Execution Plan
Executing an execution plan consists of two tasks that may be performed in parallel. The first task is simply the execution of the non-deferred grouped field set. The second task is to use the partitioned grouped field sets within the execution plan to generate Execution Groups, i.e. Incremental Data Records, where each Incremental Data Records represents the deferred execution of one of the partitioned grouped field sets.
- If path is not provided, initialize it to an empty list.
- Let newDeferMap be the result of GetNewDeferMap(newDeferUsages, path, deferMap).
- Let groupedFieldSet and newGroupedFieldSets be the corresponding entries on executionPlan.
- Allowing for parallelization, perform the following steps:
- Let data and nestedIncrementalDataRecords be the result of running ExecuteGroupedFieldSet(groupedFieldSet, objectType, objectValue, variableValues, path, deferUsageSet, newDeferMap) serially if serial is true, normally (allowing parallelization) otherwise.
- Let incrementalDataRecords be the result of CollectExecutionGroups(objectType, objectValue, variableValues, newGroupedFieldSets, path, newDeferMap).
- Append all items in nestedIncrementalDataRecords to incrementalDataRecords.
- Return data and incrementalDataRecords.
6.4.1Mapping @defer Directives to Delivery Groups
Because @defer
directives may be nested within list types, a map is required to associate a Defer Usage record as recorded within Field Details Records and an actual Deferred Fragment so that any additional Execution Groups may be associated with the correct Deferred Fragment. The GetNewDeferMap() algorithm creates that map. Given a list of new Defer Usages, the actual path at which the fields they defer are spread, and an initial map, it returns a new map containing all entries in the provided defer map, as well as new entries for each new Defer Usage.
- If newDeferUsages is empty, return deferMap:
- Let newDeferMap be a new unordered map containing all entries in deferMap.
- For each deferUsage in newDeferUsages:
- Let parentDeferUsage and label be the corresponding entries on deferUsage.
- Let parent be the entry in deferMap for parentDeferUsage.
- Let newDeferredFragment be an unordered map containing parent, path and label.
- Set the entry for deferUsage in newDeferMap to newDeferredFragment.
- Return newDeferMap.
6.4.2Collecting Execution Groups
The CollectExecutionGroups() algorithm is responsible for creating the Execution Groups, i.e. Incremental Data Records, for each partitioned grouped field set. It uses the map created by GetNewDeferMap() algorithm to associate each Execution Group with the correct Deferred Fragment.
- Initialize incrementalDataRecords to an empty list.
- For each deferUsageSet and groupedFieldSet in newGroupedFieldSets:
- Let deferredFragments be an empty list.
- For each deferUsage in deferUsageSet:
- Let deferredFragment be the entry for deferUsage in deferMap.
- Append deferredFragment to deferredFragments.
- Let incrementalDataRecord represent the future execution of ExecuteExecutionGroup(groupedFieldSet, objectType, objectValue, variableValues, deferredFragments, path, deferUsageSet, deferMap), incrementally completing deferredFragments at path.
- Append incrementalDataRecord to incrementalDataRecords.
- Schedule initiation of execution of incrementalDataRecord following any implementation specific deferral.
- Return incrementalDataRecords.
The ExecuteExecutionGroup() algorithm is responsible for actually executing the deferred grouped field set and collecting the result and any raised errors.
- Let data and incrementalDataRecords be the result of running ExecuteGroupedFieldSet(groupedFieldSet, objectType, objectValue, variableValues, path, deferUsageSet, deferMap) normally (allowing parallelization).
- Let errors be the list of all field error raised while executing ExecuteGroupedFieldSet().
- Return an unordered map containing data, errors, and incrementalDataRecords.
6.5Executing a Grouped Field Set
To execute a grouped field set, the object value being evaluated and the object type need to be known, as well as whether it must be executed serially, or may be executed in parallel.
Each represented field in the grouped field set produces an entry into a response map.
- Initialize resultMap to an empty ordered map.
- Initialize incrementalDataRecords to an empty list.
- For each groupedFieldSet as responseKey and fields:
- Let fieldName be the name of the first entry in fields. Note: This value is unaffected if an alias is used.
- Let fieldType be the return type defined for the field fieldName of objectType.
- If fieldType is defined:
- Let responseValue and fieldIncrementalDataRecords be the result of ExecuteField(objectType, objectValue, fieldType, fields, variableValues, path, deferUsageSet, deferMap).
- Set responseValue as the value for responseKey in resultMap.
- Append all items in fieldIncrementalDataRecords to incrementalDataRecords.
- Return resultMap and incrementalDataRecords.
Errors and Non-Null Fields
If during ExecuteGroupedFieldSet() a field with a non-null fieldType raises a field error then that error must propagate to this entire selection set, either resolving to null if allowed or further propagated to a parent field.
If this occurs, any sibling fields which have not yet executed or have not yet yielded a value may be cancelled to avoid unnecessary work.
6.5.1Normal and Serial Execution
Normally the executor can execute the entries in a grouped field set in whatever order it chooses (normally in parallel). Because the resolution of fields other than top-level mutation fields must always be side effect-free and idempotent, the execution order must not affect the result, and hence the service has the freedom to execute the field entries in whatever order it deems optimal.
For example, given the following grouped field set to be executed normally:
Example № 201{
birthday {
month
}
address {
street
}
}
A valid GraphQL executor can resolve the four fields in whatever order it chose (however of course birthday
must be resolved before month
, and address
before street
).
When executing a mutation, the selections in the top most selection set will be executed in serial order, starting with the first appearing field textually.
When executing a grouped field set serially, the executor must consider each entry from the grouped field set in the order provided in the grouped field set. It must determine the corresponding entry in the result map for each item to completion before it continues on to the next item in the grouped field set:
For example, given the following mutation operation, the root selection set must be executed serially:
Example № 202mutation ChangeBirthdayAndAddress($newBirthday: String!, $newAddress: String!) {
changeBirthday(birthday: $newBirthday) {
month
}
changeAddress(address: $newAddress) {
street
}
}
Therefore the executor must, in serial:
- Run ExecuteField() for
changeBirthday
, which during CompleteValue() will execute the{ month }
sub-selection set normally. - Run ExecuteField() for
changeAddress
, which during CompleteValue() will execute the{ street }
sub-selection set normally.
As an illustrative example, let’s assume we have a mutation field changeTheNumber
that returns an object containing one field, theNumber
. If we execute the following selection set serially:
Example № 203# Note: This is a selection set, not a full document using the query shorthand.
{
first: changeTheNumber(newNumber: 1) {
theNumber
}
second: changeTheNumber(newNumber: 3) {
theNumber
}
third: changeTheNumber(newNumber: 2) {
theNumber
}
}
The executor will execute the following serially:
- Resolve the
changeTheNumber(newNumber: 1)
field - Execute the
{ theNumber }
sub-selection set offirst
normally - Resolve the
changeTheNumber(newNumber: 3)
field - Execute the
{ theNumber }
sub-selection set ofsecond
normally - Resolve the
changeTheNumber(newNumber: 2)
field - Execute the
{ theNumber }
sub-selection set ofthird
normally
A correct executor must generate the following result for that selection set:
Example № 204{
"first": {
"theNumber": 1
},
"second": {
"theNumber": 3
},
"third": {
"theNumber": 2
}
}
6.5.2Field Collection
Before execution, the selection set is converted to a grouped field set by calling CollectFields(). Each entry in the grouped field set is a list of fields that share a response key (the alias if defined, otherwise the field name). This ensures all fields with the same response key (including those in referenced fragments) are executed at the same time.
As an example, collecting the fields of this selection set would collect two instances of the field a
and one of field b
:
Example № 205{
a {
subfield1
}
...ExampleFragment
}
fragment ExampleFragment on Query {
a {
subfield2
}
b
}
The depth-first-search order of the field groups produced by CollectFields() is maintained through execution, ensuring that fields appear in the executed response in a stable and predictable order.
The CollectFields() algorithm makes use of the following data types:
Defer Usage Records are unordered maps representing the usage of a @defer
directive within a given operation. Defer Usages are “abstract” in that they include information about the @defer
directive from the AST of the GraphQL document. A single Defer Usage may be used to create many “concrete” Delivery Groups when a @defer
is included within a list type.
Defer Usages contain the following information:
- label: the
label
argument provided by the given@defer
directive, if any, otherwise undefined. - parentDeferUsage: a Defer Usage corresponding to the
@defer
directive enclosing this@defer
directive, if any, otherwise undefined.
The parentDeferUsage entry is used to build distinct Execution Groups as discussed within the Execution Plan Generation section below.
Field Details Records are unordered maps containing the following entries:
- field: the Field selection.
- deferUsage: the Defer Usage enclosing the selection, if any, otherwise undefined.
A Grouped Field Set is an ordered map of keys to lists of Field Details. The keys are the same as that of the response, the alias for the field, if defined, otherwise the field name.
The CollectFields() algorithm returns:
- groupedFieldSet: the Grouped Field Set for the fields in the selection set.
- newDeferUsages: a list of new Defer Usages encountered during this field collection.
- If visitedFragments is not provided, initialize it to the empty set.
- Initialize groupedFields to an empty ordered map of lists.
- Initialize newDeferUsages to an empty list.
- For each selection in selectionSet:
- If selection provides the directive
@skip
, let skipDirective be that directive.- If skipDirective‘s if argument is true or is a variable in variableValues with the value true, continue with the next selection in selectionSet.
- If selection provides the directive
@include
, let includeDirective be that directive.- If includeDirective‘s if argument is not true and is not a variable in variableValues with the value true, continue with the next selection in selectionSet.
- If selection is a Field:
- Let responseKey be the response key of selection (the alias if defined, otherwise the field name).
- Let fieldDetails be a new unordered map containing deferUsage.
- Set the entry for field on fieldDetails to selection. and deferUsage.
- Let groupForResponseKey be the list in groupedFields for responseKey; if no such list exists, create it as an empty list.
- Append fieldDetails to the groupForResponseKey.
- If selection is a FragmentSpread:
- Let fragmentSpreadName be the name of selection.
- If fragmentSpreadName provides the directive
@defer
and its if argument is not false and is not a variable in variableValues with the value false:- Let deferDirective be that directive.
- If this execution is for a subscription operation, raise a field error.
- If deferDirective is not defined:
- If fragmentSpreadName is in visitedFragments, continue with the next selection in selectionSet.
- Add fragmentSpreadName to visitedFragments.
- Let fragment be the Fragment in the current Document whose name is fragmentSpreadName.
- If no such fragment exists, continue with the next selection in selectionSet.
- Let fragmentType be the type condition on fragment.
- If DoesFragmentTypeApply(objectType, fragmentType) is false, continue with the next selection in selectionSet.
- Let fragmentSelectionSet be the top-level selection set of fragment.
- If deferDirective is defined:
- Let label be the corresponding entry on deferDirective.
- Let parentDeferUsage be deferUsage.
- Let fragmentDeferUsage be an unordered map containing label and parentDeferUsage.
- Otherwise, let fragmentDeferUsage be deferUsage.
- Let fragmentGroupedFieldSet and fragmentNewDeferUsages be the result of calling CollectFields(objectType, fragmentSelectionSet, variableValues, fragmentDeferUsage, visitedFragments).
- For each fragmentGroup in fragmentGroupedFieldSet:
- Let responseKey be the response key shared by all fields in fragmentGroup.
- Let groupForResponseKey be the list in groupedFields for responseKey; if no such list exists, create it as an empty list.
- Append all items in fragmentGroup to groupForResponseKey.
- Append all items in fragmentNewDeferUsages to newDeferUsages.
- If selection is an InlineFragment:
- Let fragmentType be the type condition on selection.
- If fragmentType is not null and DoesFragmentTypeApply(objectType, fragmentType) is false, continue with the next selection in selectionSet.
- Let fragmentSelectionSet be the top-level selection set of selection.
- If InlineFragment provides the directive
@defer
and its if argument is not false and is not a variable in variableValues with the value false:- Let deferDirective be that directive.
- If this execution is for a subscription operation, raise a field error.
- If deferDirective is defined:
- Let label be the corresponding entry on deferDirective.
- Let parentDeferUsage be deferUsage.
- Let fragmentDeferUsage be an unordered map containing label and parentDeferUsage.
- Otherwise, let fragmentDeferUsage be deferUsage.
- Let fragmentGroupedFieldSet and fragmentNewDeferUsages be the result of calling CollectFields(objectType, fragmentSelectionSet, variableValues, fragmentDeferUsage, visitedFragments).
- For each fragmentGroup in fragmentGroupedFieldSet:
- Let responseKey be the response key shared by all fields in fragmentGroup.
- Let groupForResponseKey be the list in groupedFields for responseKey; if no such list exists, create it as an empty list.
- Append all items in fragmentGroup to groupForResponseKey.
- Append all items in fragmentNewDeferUsages to newDeferUsages.
- If selection provides the directive
- Return groupedFields and newDeferUsages.
- If fragmentType is an Object Type:
- If objectType and fragmentType are the same type, return true, otherwise return false.
- If fragmentType is an Interface Type:
- If objectType is an implementation of fragmentType, return true otherwise return false.
- If fragmentType is a Union:
- If objectType is a possible type of fragmentType, return true otherwise return false.
@skip
and @include
directives may be applied in either order since they apply commutatively.6.5.3Execution Plan Generation
A grouped field set may contain fields that have been deferred by the use of the @defer
directive on their enclosing fragments. Given a grouped field set, BuildExecutionPlan() generates an execution plan by partitioning the grouped field as specified by the operation’s use of @defer
and the requirements of the incremental response format. An execution plan consists of a single new grouped field containing the fields that do not require deferral, and a map of new grouped field set containing where the keys represent the set of Defer Usages containing those fields.
- If parentDeferUsages is not provided, initialize it to the empty set.
- Initialize groupedFieldSet to an empty ordered map.
- Initialize newGroupedFieldSets to an empty unordered map.
- Let executionPlan be an unordered map containing groupedFieldSet and newGroupedFieldSets.
- For each responseKey and groupForResponseKey of groupedFieldSet:
- Let filteredDeferUsageSet be the result of GetFilteredDeferUsageSet(groupForResponseKey).
- If filteredDeferUsageSet is the equivalent set to parentDeferUsages:
- Set the entry for responseKey in groupedFieldSet to groupForResponseKey.
- Otherwise:
- Let newGroupedFieldSet be the entry in newGroupedFieldSets for any equivalent set to deferUsageSet; if no such map exists, create it as an empty ordered map.
- Set the entry for responseKey in newGroupedFieldSet to groupForResponseKey.
- Return executionPlan.
- Initialize filteredDeferUsageSet to the empty set.
- For each fieldDetails of fieldDetailsList:
- Let deferUsage be the corresponding entry on fieldDetails.
- If deferUsage is not defined:
- Remove all entries from filteredDeferUsageSet.
- Return filteredDeferUsageSet.
- Add deferUsage to filteredDeferUsageSet.
- For each deferUsage in filteredDeferUsageSet:
- Let parentDeferUsage be the corresponding entry on deferUsage.
- While parentDeferUsage is defined:
- If parentDeferUsage is contained by filteredDeferUsageSet:
- Remove deferUsage from filteredDeferUsageSet.
- Continue to the next deferUsage in filteredDeferUsageSet.
- Reset parentDeferUsage to the corresponding entry on parentDeferUsage.
- If parentDeferUsage is contained by filteredDeferUsageSet:
- Return filteredDeferUsageSet.
6.6Executing Fields
Each field requested in the grouped field set that is defined on the selected objectType will result in an entry in the response map. Field execution first coerces any provided argument values, then resolves a value for the field, and finally completes that value either by recursively executing another selection set or coercing a scalar value.
- Let fieldDetails be the first entry in fieldDetailsList.
- Let field be the corresponding entry on fieldDetails.
- Let fieldName be the field name of field.
- Append fieldName to path.
- Let argumentValues be the result of CoerceArgumentValues(objectType, field, variableValues).
- Let resolvedValue be ResolveFieldValue(objectType, objectValue, fieldName, argumentValues).
- Return the result of CompleteValue(fieldType, fields, resolvedValue, variableValues, path, deferUsageSet, deferMap).
6.6.1Coercing Field Arguments
Fields may include arguments which are provided to the underlying runtime in order to correctly produce a value. These arguments are defined by the field in the type system to have a specific input type.
At each argument position in an operation may be a literal Value, or a Variable to be provided at runtime.
- Let coercedValues be an empty unordered Map.
- Let argumentValues be the argument values provided in field.
- Let fieldName be the name of field.
- Let argumentDefinitions be the arguments defined by objectType for the field named fieldName.
- For each argumentDefinition in argumentDefinitions:
- Let argumentName be the name of argumentDefinition.
- Let argumentType be the expected type of argumentDefinition.
- Let defaultValue be the default value for argumentDefinition.
- Let hasValue be true if argumentValues provides a value for the name argumentName.
- Let argumentValue be the value provided in argumentValues for the name argumentName.
- If argumentValue is a Variable:
- Let variableName be the name of argumentValue.
- Let hasValue be true if variableValues provides a value for the name variableName.
- Let value be the value provided in variableValues for the name variableName.
- Otherwise, let value be argumentValue.
- If hasValue is not true and defaultValue exists (including null):
- Add an entry to coercedValues named argumentName with the value defaultValue.
- Otherwise if argumentType is a Non-Nullable type, and either hasValue is not true or value is null, raise a field error.
- Otherwise if hasValue is true:
- If value is null:
- Add an entry to coercedValues named argumentName with the value null.
- Otherwise, if argumentValue is a Variable:
- Add an entry to coercedValues named argumentName with the value value.
- Otherwise:
- If value cannot be coerced according to the input coercion rules of argumentType, raise a field error.
- Let coercedValue be the result of coercing value according to the input coercion rules of argumentType.
- Add an entry to coercedValues named argumentName with the value coercedValue.
- If value is null:
- Return coercedValues.
6.6.2Value Resolution
While nearly all of GraphQL execution can be described generically, ultimately the internal system exposing the GraphQL interface must provide values. This is exposed via ResolveFieldValue, which produces a value for a given field on a type for a real value.
As an example, this might accept the objectType Person
, the field "soulMate", and the objectValue representing John Lennon. It would be expected to yield the value representing Yoko Ono.
- Let resolver be the internal function provided by objectType for determining the resolved value of a field named fieldName.
- Return the result of calling resolver, providing objectValue and argumentValues.
6.6.3Value Completion
After resolving the value for a field, it is completed by ensuring it adheres to the expected return type. If the return type is another Object type, then the field execution process continues recursively.
- If the fieldType is a Non-Null type:
- Let innerType be the inner type of fieldType.
- Let completedResult and incrementalDataRecords be the result of calling CompleteValue(innerType, fields, result, variableValues, path).
- If completedResult is null, raise a field error.
- Return completedResult and incrementalDataRecords.
- If result is null (or another internal value similar to null such as undefined), return null.
- If fieldType is a List type:
- If result is not a collection of values, raise a field error.
- Let innerType be the inner type of fieldType.
- Return the result of CompleteListValue(innerType, fieldDetailsList, result, variableValues, path, deferUsageSet, deferMap).
- If fieldType is a Scalar or Enum type:
- Return the result of CoerceResult(fieldType, result).
- If fieldType is an Object, Interface, or Union type:
- If fieldType is an Object type.
- Let objectType be fieldType.
- Otherwise if fieldType is an Interface or Union type.
- Let objectType be ResolveAbstractType(fieldType, result).
- Let groupedFieldSet and newDeferUsages be the result of calling CollectSubfields(objectType, fieldDetailsList, variableValues).
- Let executionPlan be the result of BuildExecutionPlan(groupedFieldSet, deferUsageSet).
- Return the result of ExecuteExecutionPlan(newDeferUsages, executionPlan, objectType, result, variableValues, false, path, deferUsageSet, deferMap).
- If fieldType is an Object type.
- Initialize items and incrementalDataRecords to empty lists.
- Let index be 0.
- For each resultItem of result:
- Let itemPath be path with index appended.
- Let completedItem and itemIncrementalDataRecords be the result of calling CompleteValue(innerType, fieldDetailsList, item, variableValues, itemPath).
- Append completedItem to items.
- Append all items in itemIncrementalDataRecords to incrementalDataRecords.
- Increment index by 1.
- Return items and incrementalDataRecords.
Coercing Results
The primary purpose of value completion is to ensure that the values returned by field resolvers are valid according to the GraphQL type system and a service’s schema. This “dynamic type checking” allows GraphQL to provide consistent guarantees about returned types atop any service’s internal runtime.
See the Scalars Result Coercion and Serialization sub-section for more detailed information about how GraphQL’s built-in scalars coerce result values.
- Assert value is not null.
- Return the result of calling the internal method provided by the type system for determining the “result coercion” of leafType given the value value. This internal method must return a valid value for the type and not null. Otherwise raise a field error.
Resolving Abstract Types
When completing a field with an abstract return type, that is an Interface or Union return type, first the abstract type must be resolved to a relevant Object type. This determination is made by the internal system using whatever means appropriate.
- Return the result of calling the internal method provided by the type system for determining the Object type of abstractType given the value objectValue.
Merging Selection Sets
When more than one field of the same name is executed in parallel, during value completion their selection sets are collected together to produce a single grouped field set in order to continue execution of the sub-selection sets.
An example operation illustrating parallel fields with the same name with sub-selections.
Example № 206{
me {
firstName
}
me {
lastName
}
}
After resolving the value for me
, the selection sets are merged together so firstName
and lastName
can be resolved for one value.
- Initialize groupedFieldSet to an empty ordered map of lists.
- Initialize newDeferUsages to an empty list.
- For each fieldDetails in fieldDetailsList:
- Let field and deferUsage be the corresponding entries on fieldDetails.
- Let fieldSelectionSet be the selection set of field.
- If fieldSelectionSet is null or empty, continue to the next field.
- Let subGroupedFieldSet and subNewDeferUsages be the result of CollectFields(objectType, fieldSelectionSet, variableValues, deferUsage).
- For each subGroupedFieldSet as responseKey and subfields:
- Let groupForResponseKey be the list in groupedFieldSet for responseKey; if no such list exists, create it as an empty list.
- Append all fields in subfields to groupForResponseKey.
- Append all defer usages in subNewDeferUsages to newDeferUsages.
- Return groupedFieldSet and newDeferUsages.
6.6.4Handling Field Errors
A field error is an error raised from a particular field during value resolution or coercion. While these errors should be reported in the response, they are “handled” by producing a partial response.
If a field error is raised while resolving a field, it is handled as though the field returned null, and the 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 a field error was raised), and that field is of a Non-Null
type, then a field error is raised. The error must be added to the "errors" list in the response.
If the field returns null because of a field error which has already been added to the "errors" list in the response, the "errors" list must not be further affected. That is, only one error should be added to the errors list per field.
Since Non-Null
type fields cannot be null, field errors are propagated to be handled by the parent field. If the parent field may be null then it resolves to null, otherwise if it is a Non-Null
type, the field error is further propagated to its parent field.
If a List
type wraps a Non-Null
type, and one of the elements of that list resolves to null, then the entire list must resolve to null. If the List
type is also wrapped in a Non-Null
, the field error continues to propagate upwards.
If all fields from the root of the request to the source of the field error return Non-Null
types, then the "data" entry in the response should be null.
7Response
When a GraphQL service receives a request, it must return a well-formed response. The service’s response describes the result of executing the requested operation if successful, and describes any errors raised during the request.
A response may contain both a partial response as well as a list of errors in the case that any field error was raised on a field and was replaced with null.
7.1Response Format
A response to a GraphQL request must be a map or a stream of incrementally delivered results. The response will be a stream of incrementally delivered results when the GraphQL service has deferred or streamed data as a result of the @defer
or @stream
directives. When the response of the GraphQL operation contains incrementally delivered results, the first value will be an initial payload, followed by one or more subsequent payloads.
If the request raised any errors, the response map must contain an entry with key errors
. The value of this entry is described in the “Errors” section. If the request completed without raising any errors, this entry must not be present.
If the request included execution, the response map must contain an entry with key data
. The value of this entry is described in the “Data” section. If the request failed before execution, due to a syntax error, missing information, or validation error, this entry must not be present.
When the response of the GraphQL operation contains incrementally delivered results, both the initial payload and all subsequent payloads must contain an entry with key hasNext
. The value of this entry must be true for all but the last response in the stream. The value of this entry must be false for the last response of the stream. This entry must not be present for GraphQL operations that return a single response map.
When the response of the GraphQL operation contains incrementally delivered results, both the initial payload and any subsequent payloads may contain entries with the keys pending
, incremental
, and/or completed
. The value of these entries are described in the “Pending”, “Incremental”, and “Completed” sections below.
The response map may also contain an entry with key extensions
. This entry, if set, must have a map as its value. This entry is reserved for implementers to extend the protocol however they see fit, and hence there are no additional restrictions on its contents. When the response of the GraphQL operation is a response stream, the initial payload and any subsequent payloads may contain an entry with the key extensions
, also reserved for implementers to extend the protocol however they see fit. Additionally, implementers may send subsequent payloads containing only hasNext
and extensions
entries.
To ensure future changes to the protocol do not break existing services and clients, the top level response map must not contain any entries other than the entries described above.
errors
is present in the response, it may be helpful for it to appear first when serialized to make it more clear when errors are present in a response during debugging.7.1.1Data
The data
entry in the response will be the result of the execution of the requested operation. If the operation was a query, this output will be an object of the query root operation type; if the operation was a mutation, this output will be an object of the mutation root operation type.
If an error was raised before execution begins, the data
entry should not be present in the result.
If an error was raised during the execution that prevented a valid response, the data
entry in the response should be null
.
When the response of the GraphQL operation contains incrementally delivered results, data
may only be present in the initial payload. data
must not be present in any subsequent payloads.
7.1.2Errors
The errors
entry in the response is a non-empty list of errors raised during the request, where each error is a map of data described by the error result format below.
If present, the errors
entry in the response must contain at least one error. If no errors were raised during the request, the errors
entry must not be present in the result.
If the data
entry in the response is not present, the errors
entry must be present. It must contain at least one request error indicating why no data was able to be returned.
If the data
entry in the response is present (including if it is the value null), the errors
entry must be present if and only if one or more field error was raised during execution.
Request Errors
A request error is an error raised during a request which results in no response data. Typically raised before execution begins, a request error may occur due to a parse grammar or validation error in the Document, an inability to determine which operation to execute, or invalid input values for variables.
A request error is typically the fault of the requesting client.
If a request error is raised, the data
entry in the response must not be present, the errors
entry must include the error, and request execution should be halted.
Field Errors
A field error is an error raised during the execution of a particular field which results in partial response data. This may occur due to an internal error during value resolution or failure to coerce the resulting value.
A field error is typically the fault of a GraphQL service.
If a field error is raised, execution attempts to continue and a partial result is produced (see Handling Field Errors). The data
entry in the response must be present. The errors
entry should include this error.
Error Result Format
Every error must contain an entry with the key message
with a string description of the error intended for the developer as a guide to understand and correct the error.
If an error can be associated to a particular point in the requested GraphQL document, it should contain an entry with the key locations
with a list of locations, where each location is a map with the keys line
and column
, both positive numbers starting from 1
which describe the beginning of an associated syntax element.
If an error can be associated to a particular field in the GraphQL result, it must contain an entry with the key path
that details the path of the response field which experienced the error. This allows clients to identify whether a null
result is intentional or caused by a runtime error. The value of this field is described in the Path section.
For example, if fetching one of the friends’ names fails in the following operation:
Example № 207{
hero(episode: $episode) {
name
heroFriends: friends {
id
name
}
}
}
The response might look like:
Example № 208{
"errors": [
{
"message": "Name for character with ID 1002 could not be fetched.",
"locations": [{ "line": 6, "column": 7 }],
"path": ["hero", "heroFriends", 1, "name"]
}
],
"data": {
"hero": {
"name": "R2-D2",
"heroFriends": [
{
"id": "1000",
"name": "Luke Skywalker"
},
{
"id": "1002",
"name": null
},
{
"id": "1003",
"name": "Leia Organa"
}
]
}
}
}
If the field which experienced an error was declared as Non-Null
, the null
result will bubble up to the next nullable field. In that case, the path
for the error should include the full path to the result field where the error was raised, even if that field is not present in the response.
For example, if the name
field from above had declared a Non-Null
return type in the schema, the result would look different but the error reported would be the same:
Example № 209{
"errors": [
{
"message": "Name for character with ID 1002 could not be fetched.",
"locations": [{ "line": 6, "column": 7 }],
"path": ["hero", "heroFriends", 1, "name"]
}
],
"data": {
"hero": {
"name": "R2-D2",
"heroFriends": [
{
"id": "1000",
"name": "Luke Skywalker"
},
null,
{
"id": "1003",
"name": "Leia Organa"
}
]
}
}
}
GraphQL services may provide an additional entry to errors with key extensions
. This entry, if set, must have a map as its value. This entry is reserved for implementers to add additional information to errors however they see fit, and there are no additional restrictions on its contents.
Example № 210{
"errors": [
{
"message": "Name for character with ID 1002 could not be fetched.",
"locations": [{ "line": 6, "column": 7 }],
"path": ["hero", "heroFriends", 1, "name"],
"extensions": {
"code": "CAN_NOT_FETCH_BY_ID",
"timestamp": "Fri Feb 9 14:33:09 UTC 2018"
}
}
]
}
GraphQL services should not provide any additional entries to the error format since they could conflict with additional entries that may be added in future versions of this specification.
extensions
entry for error formatting. While non-specified entries are not violations, they are still discouraged.Counter Example № 211{
"errors": [
{
"message": "Name for character with ID 1002 could not be fetched.",
"locations": [{ "line": 6, "column": 7 }],
"path": ["hero", "heroFriends", 1, "name"],
"code": "CAN_NOT_FETCH_BY_ID",
"timestamp": "Fri Feb 9 14:33:09 UTC 2018"
}
]
}
7.1.3Path
A path
field allows for the association to a particular field in a GraphQL result. This field should be a list of path segments starting at the root of the response and ending with the field to be associated with. Path segments that represent fields should be strings, and path segments that represent list indices should be 0-indexed integers. If the path is associated to an aliased field, the path should use the aliased name, since it represents a path in the response, not in the request.
When the path
field is present on an “Error result”, it indicates the response field which experienced the error.
7.1.4Pending
The pending
entry in the response is a non-empty list of Pending Results. If the response of the GraphQL operation contains incrementally delivered results, this field may appear on both the initial and subsequent payloads. If present, the pending
entry must contain at least one Pending Result.
Each Pending Result corresponds to either a @defer
or @stream
directive located at a specific path in the response data. The Pending Result is used to communicate that the GraphQL service has chosen to incrementally deliver the data associated with this @defer
or @stream
directive and clients should expect the associated data in either the current payload, or one of the following payloads.
Pending Result Format
Every Pending Result must contain an entry with the key id
with a string value. This id
should be used by clients to correlate Pending Results with Completed Results. The id
value must be unique for the entire response stream. There must not be any other Pending Result in any payload that contains the same id
.
Every Pending Result must contain an entry with the key path
. When the Pending Result is associated with a @stream
directive, it indicates the response list field that is not known to be complete. Clients should expect the GraphQL Service to incrementally deliver the remainder of indicated list field. When the Pending Result is associated with a @defer
directive, it indicates that the response fields contained in the deferred fragment are not known to be complete. Clients should expect the the GraphQL Service to incrementally deliver the remainder of the fields contained in the deferred fragment.
If the associated @defer
or @stream
directive contains a label
argument, the Pending Result must contain an entry label
with the value of this argument.
If a Pending Result is not returned for a @defer
or @stream
directive, clients must assume that the GraphQL service chose not to incrementally deliver this data, and the data can be found either in the data
entry in the initial payload, or one of the Incremental Results in a prior payload.
7.1.5Incremental
The incremental
entry in the response is a non-empty list of Incremental Results. If the response of the GraphQL operation contains incrementally delivered results, this field may appear on both the initial and subsequent values. If present, the incremental
entry must contain at least one Incremental Result.
The Incremental Result is used to deliver data that the GraphQL service has chosen to incrementally deliver. An Incremental Result may be ether an Incremental List Result or an Incremental Object Result.
An Incremental List Result is used to deliver additional list items for a list field with a @stream
directive.
An Incremental Object Result is used to deliver additional response fields that were contained in one or more fragments with a @defer
directive.
Incremental Result Format
Every Incremental Result must contain an entry with the key id
with a string value. This id
must match the id
that was returned in a prior Pending Result.
Additionally, Incremental List Results and Incremental Object Results have further requirements.
Incremental List Result Format
An Incremental List Result’s id
entry must match the id
that was returned in a prior Pending Result. This Pending Result must be associated with a @stream
directive.
The Incremental List Result’s path
can be determined using the prior Pending Result with the same id
as this Incremental Result. The Incremental List Result’s path
is the same as the Pending Result’s path
.
Every Incremental List Result must contain an items
entry. The items
entry must contain a list of additional list items for the response field at the Incremental List Result’s path
. This output will be a list of the same type of the response field at this path.
If any field errors were raised during the execution of the results in items
and these errors bubbled to a path higher than the Incremental List Result’s path, The Incremental List Result is considered failed and should not be included in the response stream. The errors that caused this failure will be included in a Completed Result.
If any field errors were raised during the execution of the results in items
and these errors did not bubble to a path higher than the Incremental List Result’s path, the Incremental List Result must contain an entry with key errors
containing these field errors. The value of this entry is described in the “Errors” section.
Incremental Object Result Format
An Incremental List Result’s id
entry must match the id
that was returned in a prior Pending Result. This Pending Result must be associated with a @defer
directive.
The Incremental Object Result’s path
can be determined using the prior Pending Result with the same id
as this Incremental Result. The Incremental Object Result may contain a subPath
entry. If the subPath
entry is present, The Incremental Object Record’s path can be determined by concatenating the Pending Result’s path
with this subPath
. If no subPath
entry is present, the path is the same as the Pending Result’s path
.
Every Incremental Object Result must contain an data
entry. The data
entry must contain a map of additional response fields. The data
entry in an Incremental Object Result will be of the type of a particular field in the GraphQL result. The Incremental Object Result’s path
will contain the path segments of the field this data is associated with.
An Incremental Object Result’s data may contain response fields that were contained in more than one deferred fragments. In that case, the id
of the Incremental Object Result must point to the Pending Result that results in the shortest subPath
.
If any field errors were raised during the execution of the results in data
and these errors bubbled to a path higher than the Incremental Object Result’s path, The Incremental Object Result is considered failed and should not be included in the response stream. The errors that caused this failure will be included in a Completed Result.
If any field errors were raised during the execution of the results in data
and these errors did not bubble to a path higher than the Incremental Object Result’s path, the Incremental Object Result must contain an entry with key errors
containing these field errors. The value of this entry is described in the “Errors” section.
7.1.6Completed
The completed
entry in the response is a non-empty list of Completed Results. If the response of the GraphQL operation contains incrementally delivered results, this field may appear on both the initial and subsequent payloads. If present, the completed
entry must contain at least one Completed Result.
Each Completed Result corresponds to a prior Pending Result. The Completed Result is used to communicate that the GraphQL service has completed the incremental delivery of the data associated with the corresponding Pending Result. The associated data must have been completed in the current payload.
Completed Result Format
Every Completed Result must contain an entry with the key id
with a string value. The id
entry must match the id
that was returned in a prior Pending Result.
A Completed Result may contain an errors
entry. When the errors
entry is present, it informs clients that the delivery of the data associated with the corresponding Pending Result has failed, due to an error bubbling to a path higher than the Incremental Data Result’s path. The errors
entry must contain these field errors. The value of this entry is described in the “Errors” section.
7.1.7Examples
7.1.7.1A query containing both defer and stream:
Example № 212query {
person(id: "cGVvcGxlOjE=") {
...HomeWorldFragment @defer(label: "homeWorldDefer")
name
films @stream(initialCount: 1, label: "filmsStream") {
title
}
}
}
fragment HomeWorldFragment on Person {
homeWorld {
name
}
}
The response stream might look like:
Payload 1, the initial response does not contain any deferred or streamed results in the data
entry. The initial response contains a hasNext
entry, indicating that subsequent payloads will be delivered. There are two Pending Responses indicating that results for both the @defer
and @stream
in the query will be delivered in the subsequent payloads.
Example № 213{
"data": {
"person": {
"name": "Luke Skywalker",
"films": [{ "title": "A New Hope" }]
}
},
"pending": [
{ "id": "0", "path": ["person"], "label": "homeWorldDefer" },
{ "id": "1", "path": ["person", "films"], "label": "filmsStream" }
],
"hasNext": true
}
Payload 2, contains the deferred data and the first streamed list item. There is one Completed Result, indicating that the deferred data has been completely delivered.
Example № 214{
"incremental": [
{
"id": "0",
"data": { "homeWorld": { "name": "Tatooine" } }
},
{
"id": "1",
"items": [{ "title": "The Empire Strikes Back" }]
}
],
"completed": [
{"id": "0"}
]
"hasNext": true
}
Payload 3, contains the final stream payload. In this example, the underlying iterator does not close synchronously so hasNext is set to true. If this iterator did close synchronously, hasNext would be set to false and this would be the final response.
Example № 215{
"incremental": [
{
"id": "1",
"items": [{ "title": "Return of the Jedi" }]
}
],
"hasNext": true
}
Payload 4, contains no incremental data. hasNext set to false indicates the end of the response stream. This response is sent when the underlying iterator of the films
field closes.
Example № 216{
"hasNext": false
}
7.1.8Examples
7.1.8.1A query containing overlapping defers:
Example № 217query {
person(id: "cGVvcGxlOjE=") {
...HomeWorldFragment @defer(label: "homeWorldDefer")
...NameAndHomeWorldFragment @defer(label: "nameAndWorld")
firstName
}
}
fragment HomeWorldFragment on Person {
homeWorld {
name
terrain
}
}
fragment NameAndHomeWorldFragment on Person {
firstName
lastName
homeWorld {
name
}
}
The response stream might look like:
Payload 1, the initial response contains the results of the firstName
field. Even though it is also present in the HomeWorldFragment
, it must be returned in the initial payload because it is also defined outside of any fragments with the @defer
directive. Additionally, There are two Pending Responses indicating that results for both @defer
s in the query will be delivered in the subsequent payloads.
Example № 218{
"data": {
"person": {
"firstName": "Luke"
}
},
"pending": [
{ "id": "0", "path": ["person"], "label": "homeWorldDefer" },
{ "id": "1", "path": ["person"], "label": "nameAndWorld" }
],
"hasNext": true
}
Payload 2, contains the deferred data from HomeWorldFragment
. There is one Completed Result, indicating that HomeWorldFragment
has been completely delivered. Because the homeWorld
field is present in two separate @defer
s, it is separated into its own Incremental Result.
The second Incremental Result contains the data for the terrain
field. This incremental result contains a subPath
property to indicate to clients that the path of this result can be determined by concatenating the path from the Pending Result with id "0"
and this subPath
entry.
Example № 219{
"incremental": [
{
"id": "0",
"data": { "homeWorld": { "name": "Tatooine" } }
},
{
"id": "0",
"subPath": ["homeWorld"],
"data": { "terrain": "desert" }
}
],
"completed": [{ "id": "0" }],
"hasNext": true
}
Payload 3, contains the remaining data from the NameAndHomeWorldFragment
. lastName
is the only remaining field that has not been delivered in a previous payload.
Example № 220{
"incremental": [
{
"id": "1",
"data": { "lastName": "Skywalker" }]
}
],
"completed": [{"id": "1"}],
"hasNext": false
}
7.2Serialization Format
GraphQL does not require a specific serialization format. However, clients should use a serialization format that supports the major primitives in the GraphQL response. In particular, the serialization format must at least support representations of the following four primitives:
- Map
- List
- String
- Null
A serialization format should also support the following primitives, each representing one of the common GraphQL scalar types, however a string or simpler primitive may be used as a substitute if any are not directly supported:
- Boolean
- Int
- Float
- Enum Value
This is not meant to be an exhaustive list of what a serialization format may encode. For example custom scalars representing a Date, Time, URI, or number with a different precision may be represented in whichever relevant format a given serialization format may support.
7.2.1JSON Serialization
JSON is the most common serialization format for GraphQL. Though as mentioned above, GraphQL does not require a specific serialization format.
When using JSON as a serialization of GraphQL responses, the following JSON values should be used to encode the related GraphQL values:
GraphQL Value | JSON Value |
---|---|
Map | Object |
List | Array |
Null | null |
String | String |
Boolean | true or false |
Int | Number |
Float | Number |
Enum Value | String |
7.2.2Serialized Map Ordering
Since the result of evaluating a selection set is ordered, the serialized Map of results should preserve this order by writing the map entries in the same order as those fields were requested as defined by selection set execution. Producing a serialized response where fields are represented in the same order in which they appear in the request improves human readability during debugging and enables more efficient parsing of responses if the order of properties can be anticipated.
Serialization formats which represent an ordered map should preserve the order of requested fields as defined by CollectFields() in the Execution section. Serialization formats which only represent unordered maps but where order is still implicit in the serialization’s textual order (such as JSON) should preserve the order of requested fields textually.
For example, if the request was { name, age }
, a GraphQL service responding in JSON should respond with { "name": "Mark", "age": 30 }
and should not respond with { "age": 30, "name": "Mark" }
.
While JSON Objects are specified as an unordered collection of key-value pairs the pairs are represented in an ordered manner. In other words, while the JSON strings { "name": "Mark", "age": 30 }
and { "age": 30, "name": "Mark" }
encode the same value, they also have observably different property orderings.
AAppendix: Notation Conventions
This specification document contains a number of notation conventions used to describe technical concepts such as language grammar and semantics as well as runtime algorithms.
This appendix seeks to explain these notations in greater detail to avoid ambiguity.
A.1Context-Free Grammar
A context-free grammar consists of a number of productions. Each production has an abstract symbol called a “non-terminal” as its left-hand side, and zero or more possible sequences of non-terminal symbols and or terminal characters as its right-hand side.
Starting from a single goal non-terminal symbol, a context-free grammar describes a language: the set of possible sequences of characters that can be described by repeatedly replacing any non-terminal in the goal sequence with one of the sequences it is defined by, until all non-terminal symbols have been replaced by terminal characters.
Terminals are represented in this document in a monospace font in two forms: a specific Unicode character or sequence of Unicode characters (ie. = or terminal), and prose typically describing a specific Unicode code-point "Space (U+0020)". Sequences of Unicode characters only appear in syntactic grammars and represent a Name token of that specific sequence.
Non-terminal production rules are represented in this document using the following notation for a non-terminal with a single definition:
While using the following notation for a production with a list of definitions:
A definition may refer to itself, which describes repetitive sequences, for example:
A.2Lexical and Syntactical Grammar
The GraphQL language is defined in a syntactic grammar where terminal symbols are tokens. Tokens are defined in a lexical grammar which matches patterns of source characters. The result of parsing a source text sequence of Unicode characters first produces a sequence of lexical tokens according to the lexical grammar which then produces abstract syntax tree (AST) according to the syntactical grammar.
A lexical grammar production describes non-terminal “tokens” by patterns of terminal Unicode characters. No “whitespace” or other ignored characters may appear between any terminal Unicode characters in the lexical grammar production. A lexical grammar production is distinguished by a two colon ::
definition.
A Syntactical grammar production describes non-terminal “rules” by patterns of terminal Tokens. WhiteSpace and other Ignored sequences may appear before or after any terminal Token. A syntactical grammar production is distinguished by a one colon :
definition.
A.3Grammar Notation
This specification uses some additional notation to describe common patterns, such as optional or repeated patterns, or parameterized alterations of the definition of a non-terminal. This section explains these short-hand notations and their expanded definitions in the context-free grammar.
Constraints
A grammar production may specify that certain expansions are not permitted by using the phrase “but not” and then indicating the expansions to be excluded.
For example, the following production means that the non-terminal SafeWord may be replaced by any sequence of characters that could replace Word provided that the same sequence of characters could not replace SevenCarlinWords.
A grammar may also list a number of restrictions after “but not” separated by “or”.
For example:
Lookahead Restrictions
A grammar production may specify that certain characters or tokens are not permitted to follow it by using the pattern NotAllowed. Lookahead restrictions are often used to remove ambiguity from the grammar.
The following example makes it clear that Letterlist must be greedy, since Word cannot be followed by yet another Letter.
Optionality and Lists
A subscript suffix “Symbolopt” is shorthand for two possible sequences, one including that symbol and one excluding it.
As an example:
is shorthand for
A subscript suffix “Symbollist” is shorthand for a list of one or more of that symbol, represented as an additional recursive production.
As an example:
is shorthand for
Parameterized Grammar Productions
A symbol definition subscript suffix parameter in braces “SymbolParam” is shorthand for two symbol definitions, one appended with that parameter name, the other without. The same subscript suffix on a symbol is shorthand for that variant of the definition. If the parameter starts with “?”, that form of the symbol is used if in a symbol definition with the same parameter. Some possible sequences can be included or excluded conditionally when respectively prefixed with “[+Param]” and “[~Param]”.
As an example:
is shorthand for
A.4Grammar Semantics
This specification describes the semantic value of many grammar productions in the form of a list of algorithmic steps.
For example, this describes how a parser should interpret a string literal:
- Return the Unicode character sequence of all StringCharacter Unicode character values.
A.5Algorithms
This specification describes some algorithms used by the static and runtime semantics, they’re defined in the form of a function-like syntax with the algorithm’s name and the arguments it accepts along with a list of algorithmic steps to take in the order listed. Each step may establish references to other values, check various conditions, call other algorithms, and eventually return a value representing the outcome of the algorithm for the provided arguments.
For example, the following example describes an algorithm named Fibonacci which accepts a single argument number. The algorithm’s steps produce the next number in the Fibonacci sequence:
BAppendix: Grammar Summary
B.1Source Text
B.2Ignored Tokens
B.3Lexical Tokens
! | $ | & | ( | ) | ... | : | = | @ | [ | ] | { | | | } |
A | B | C | D | E | F | G | H | I | J | K | L | M |
N | O | P | Q | R | S | T | U | V | W | X | Y | Z |
a | b | c | d | e | f | g | h | i | j | k | l | m |
n | o | p | q | r | s | t | u | v | w | x | y | z |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
+ | - |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
A | B | C | D | E | F | ||||
a | b | c | d | e | f |
" | \ | / | b | f | n | r | t |
B.4Document Syntax
query | mutation | subscription |
true | false |
QUERY |
MUTATION |
SUBSCRIPTION |
FIELD |
FRAGMENT_DEFINITION |
FRAGMENT_SPREAD |
INLINE_FRAGMENT |
VARIABLE_DEFINITION |
SCHEMA |
SCALAR |
OBJECT |
FIELD_DEFINITION |
ARGUMENT_DEFINITION |
INTERFACE |
UNION |
ENUM |
ENUM_VALUE |
INPUT_OBJECT |
INPUT_FIELD_DEFINITION |
§Index
- Alias
- AreTypesCompatible
- Argument
- Arguments
- ArgumentsDefinition
- BatchIncrementalResults
- BlockString
- BlockStringCharacter
- BlockStringValue
- BooleanValue
- BuildExecutionPlan
- built-in directive
- CoerceArgumentValues
- CoerceResult
- CoerceVariableValues
- CollectExecutionGroups
- CollectFields
- CollectSubfields
- Comma
- Comment
- CommentChar
- CompleteListValue
- CompleteValue
- CreateSourceEventStream
- custom directive
- default root type name
- DefaultValue
- Definition
- Description
- DetectFragmentCycles
- Digit
- Directive
- DirectiveDefinition
- DirectiveLocation
- DirectiveLocations
- Directives
- Document
- DoesFragmentTypeApply
- EnumTypeDefinition
- EnumTypeExtension
- EnumValue
- EnumValueDefinition
- EnumValuesDefinition
- EscapedCharacter
- EscapedUnicode
- ExecutableDefinition
- ExecutableDirectiveLocation
- ExecutableDocument
- ExecuteExecutionGroup
- ExecuteExecutionPlan
- ExecuteField
- ExecuteGroupedFieldSet
- ExecuteMutation
- ExecuteQuery
- ExecuteRequest
- ExecuteRootSelectionSet
- ExecuteSubscriptionEvent
- ExponentIndicator
- ExponentPart
- Field
- field error
- FieldDefinition
- FieldsDefinition
- FieldsInSetCanMerge
- FloatValue
- FractionalPart
- FragmentDefinition
- FragmentName
- FragmentSpread
- GetCompletedEntry
- GetFilteredDeferUsageSet
- GetIncrementalEntry
- GetInitialResult
- GetNewDeferMap
- GetNewRootNodes
- GetOperation
- GetPendingEntry
- GetPossibleTypes
- GetSubsequentResult
- GraphFromRecords
- GraphWithDeferredFragmentRecord
- HexDigit
- Ignored
- ImplementsInterfaces
- InlineFragment
- InputFieldsDefinition
- InputObjectTypeDefinition
- InputObjectTypeExtension
- InputValueDefinition
- IntegerPart
- InterfaceTypeDefinition
- InterfaceTypeExtension
- IntValue
- IsInputType
- IsOutputType
- IsSubType
- IsValidImplementation
- IsValidImplementationFieldType
- IsVariableUsageAllowed
- Letter
- LineTerminator
- ListType
- ListValue
- MapSourceToResponseEvent
- Name
- NameContinue
- NamedType
- NameStart
- NegativeSign
- NonNullType
- NonZeroDigit
- NullValue
- ObjectField
- ObjectTypeDefinition
- ObjectTypeExtension
- ObjectValue
- OperationDefinition
- OperationType
- Punctuator
- request
- request error
- ResolveAbstractType
- ResolveFieldEventStream
- ResolveFieldValue
- root operation type
- RootOperationTypeDefinition
- SameResponseShape
- SameStreamDirective
- scalar specification URL
- ScalarTypeDefinition
- ScalarTypeExtension
- SchemaDefinition
- SchemaExtension
- Selection
- selection set
- SelectionSet
- Sign
- SourceCharacter
- StringCharacter
- StringValue
- Subscribe
- Token
- Type
- TypeCondition
- TypeDefinition
- TypeExtension
- TypeSystemDefinition
- TypeSystemDefinitionOrExtension
- TypeSystemDirectiveLocation
- TypeSystemDocument
- TypeSystemExtension
- TypeSystemExtensionDocument
- Unicode text
- UnicodeBOM
- UnionMemberTypes
- UnionTypeDefinition
- UnionTypeExtension
- Unsubscribe
- Value
- Variable
- VariableDefinition
- VariablesDefinition
- WhiteSpace
- YieldIncrementalResults