From 7908822d4cbae5ad7136437ca577e8bc56f7efab Mon Sep 17 00:00:00 2001 From: Benjie Gillam Date: Thu, 6 Jan 2022 19:31:07 +0000 Subject: [PATCH] Format the spec with prettier (#727) * Formatting: add prettier * Format markdown --- .github/ISSUE_TEMPLATE.md | 13 +- .github/PULL_REQUEST_TEMPLATE.md | 3 +- .github/workflows/ci.yml | 8 +- .github/workflows/prettier.yaml | 12 + .prettierignore | 11 + CONTRIBUTING.md | 238 +++--- README.md | 431 +++++----- package-lock.json | 20 + package.json | 7 + signed-agreements/README.md | 4 +- spec/Appendix A -- Notation Conventions.md | 120 +-- spec/Appendix B -- Grammar Summary.md | 348 ++++---- spec/GraphQL.md | 64 +- spec/Section 1 -- Overview.md | 74 +- spec/Section 2 -- Language.md | 540 ++++++------ spec/Section 3 -- Type System.md | 936 +++++++++++---------- spec/Section 4 -- Introspection.md | 270 +++--- spec/Section 5 -- Validation.md | 700 ++++++++------- spec/Section 6 -- Execution.md | 685 +++++++-------- spec/Section 7 -- Response.md | 140 ++- 20 files changed, 2365 insertions(+), 2259 deletions(-) create mode 100644 .github/workflows/prettier.yaml create mode 100644 .prettierignore diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 2afe9fe99..0e99546f4 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -2,10 +2,15 @@ Before creating your issue: -* Have a question? Find community resources at https://graphql.org/community/ +- Have a question? Find community resources at https://graphql.org/community/ -* Find an editing mistake? Create a Pull Request with the edited fix! The Github UI allows you to edit files directly, find the source files at: https://github.com/graphql/graphql-spec/tree/master/spec +- Find an editing mistake? Create a Pull Request with the edited fix! The Github + UI allows you to edit files directly, find the source files at: + https://github.com/graphql/graphql-spec/tree/master/spec -* Improvements to documentation? Head over to https://github.com/graphql/graphql.github.io +- Improvements to documentation? Head over to + https://github.com/graphql/graphql.github.io -* Feature request? First read https://github.com/graphql/graphql-spec/blob/master/CONTRIBUTING.md and prefer creating a Pull Request! +- Feature request? First read + https://github.com/graphql/graphql-spec/blob/master/CONTRIBUTING.md and prefer + creating a Pull Request! diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 11f12f586..c6056f36c 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,3 +1,4 @@ !!! IMPORTANT !!! -Please Read https://github.com/graphql/graphql-spec/blob/master/CONTRIBUTING.md before creating a Pull Request. +Please Read https://github.com/graphql/graphql-spec/blob/master/CONTRIBUTING.md +before creating a Pull Request. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 251366ea9..43856e2a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,7 +13,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions/setup-node@v1 with: - node-version: '16.x' + node-version: "16.x" - run: npm ci - run: npm test publish: @@ -26,7 +26,7 @@ jobs: fetch-depth: 0 - uses: actions/setup-node@v1 with: - node-version: '16.x' + node-version: "16.x" - run: npm ci - run: npm run build - uses: peaceiris/actions-gh-pages@v3 @@ -34,5 +34,5 @@ jobs: github_token: ${{ secrets.GITHUB_TOKEN }} keep_files: true cname: spec.graphql.org - user_name: 'github-actions[bot]' - user_email: 'github-actions[bot]@users.noreply.github.com' + user_name: "github-actions[bot]" + user_email: "github-actions[bot]@users.noreply.github.com" diff --git a/.github/workflows/prettier.yaml b/.github/workflows/prettier.yaml new file mode 100644 index 000000000..596db3bf9 --- /dev/null +++ b/.github/workflows/prettier.yaml @@ -0,0 +1,12 @@ +name: Prettier formatting +on: [push, pull_request] + +jobs: + build: + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-node@v1 + with: + node-version: "16.x" + - run: npm ci + - run: npm run format:check diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 000000000..956060aac --- /dev/null +++ b/.prettierignore @@ -0,0 +1,11 @@ +*.swp +*~ +.*.haste_cache.* +.DS_Store +npm-debug.log +/build +/changelogs +/out +/gh-pages +/node_modules +/package.json diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 786a85d89..12638150e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -6,20 +6,19 @@ contributions. Contributions that do not change the interpretation of the spec but instead improve legibility, fix editorial errors, clear up ambiguity and improve -examples are encouraged and are often merged by a spec editor with -little process. +examples are encouraged and are often merged by a spec editor with little +process. However, contributions that _do_ meaningfully change the interpretation of the -spec must follow an RFC (Request For Comments) process led by a *champion* -through a series of *stages* intended to improve *visibility*, allow for -*discussion* to reach the best solution, and arrive at *consensus*. This process +spec must follow an RFC (Request For Comments) process led by a _champion_ +through a series of _stages_ intended to improve _visibility_, allow for +_discussion_ to reach the best solution, and arrive at _consensus_. This process becomes ever more important as GraphQL's community broadens. When proposing or weighing-in on any issue or pull request, consider the [Code of Conduct](https://github.com/graphql/foundation/blob/main/CODE-OF-CONDUCT.md) to better understand expected and unacceptable behavior. - ## Contributing to GraphQL Libraries A common point of confusion for those who wish to contribute to GraphQL is where @@ -30,216 +29,209 @@ spec first or a GraphQL library first? Admittedly, this can become a bit of a GraphQL libraries seek to be "spec compliant", which means they discourage changes that cause them to behave differently from the spec as written. However, -they also encourage pull requests for changes that accompany an RFC *proposal* -or RFC *draft*. In fact, a spec contribution RFC won't be *accepted* until it +they also encourage pull requests for changes that accompany an RFC _proposal_ +or RFC _draft_. In fact, a spec contribution RFC won't be _accepted_ until it has experience being implemented in a GraphQL library. -To allow a library to remain spec compliant while also implementing *proposals* -and *drafts*, the library's maintainers may request that these new features are +To allow a library to remain spec compliant while also implementing _proposals_ +and _drafts_, the library's maintainers may request that these new features are disabled by default with opt-in option flags or they may simply wait to merge a -well-tested pull request until the spec proposal is *accepted*. - +well-tested pull request until the spec proposal is _accepted_. ## Guiding Principles GraphQL's evolution is guided by a few principles. Suggested contributions -should use these principles to guide the details of an RFC and decisions to -move forward. See editor Lee Byron talk about +should use these principles to guide the details of an RFC and decisions to move +forward. See editor Lee Byron talk about [guiding principles at GraphQL Europe 2017](https://youtu.be/mePT9MNTM98?t=17m9s). -* **Backwards compatibility** +- **Backwards compatibility** Once a query is written, it should always mean the same thing and return the same shaped result. Future changes should not change the meaning of existing schema or requests or in any other way cause an existing compliant GraphQL service to become non-compliant for prior versions of the spec. -* **Performance is a feature** +- **Performance is a feature** GraphQL typically avoids syntax or behaviors that could jeopardize runtime efficiency, or that make demands of GraphQL services which cannot efficiently be fulfilled. -* **Favor no change** +- **Favor no change** - As GraphQL is implemented in over a dozen languages under the collaboration - of hundreds of individuals, incorporating any change has a high cost. - Accordingly, proposed changes must meet a very high bar of added value. - The burden of proof is on the contributor to illustrate this value. + As GraphQL is implemented in over a dozen languages under the collaboration of + hundreds of individuals, incorporating any change has a high cost. + Accordingly, proposed changes must meet a very high bar of added value. The + burden of proof is on the contributor to illustrate this value. -* **Enable new capabilities motivated by real use cases** +- **Enable new capabilities motivated by real use cases** Every change should intend on unlocking a real and reasonable use case. Real examples are always more compelling than theoretical ones, and common - scenarios are more compelling than rare ones. RFCs should do more than offer - a different way to reach an already achievable outcome. + scenarios are more compelling than rare ones. RFCs should do more than offer a + different way to reach an already achievable outcome. -* **Simplicity and consistency over expressiveness and terseness** +- **Simplicity and consistency over expressiveness and terseness** Plenty of behaviors and patterns found in other languages are intentionally absent from GraphQL. "Possible but awkward" is often favored over more complex alternatives. Simplicity (e.g. fewer concepts) is more important than expressing more sophisticated ideas or writing less. -* **Preserve option value** +- **Preserve option value** It's hard to know what the future brings; whenever possible, decisions should be made that allow for more options in the future. Sometimes this is unintuitive: spec rules often begin more strict than necessary with a future option to loosen when motivated by a real use case. -* **Understandability is just as important as correctness** +- **Understandability is just as important as correctness** The GraphQL spec, despite describing technical behavior, is intended to be read by people. Use natural tone and include motivation and examples. - ## RFC Contribution Champions Contributing to GraphQL requires a lot of dedicated work. To set clear expectations and provide accountability, each proposed RFC (request for -comments) must have a *champion* who is responsible for addressing feedback and -completing next steps. An RFC may have multiple *champions*. The spec editors -are not responsible for completing RFCs which lack a *champion* (though an -editor may be a *champion* for an RFC). - -An RFC which does not have a *champion* may not progress through stages, and can -become stale. Stale proposals may be picked up by a new *champion* or may -be *rejected*. +comments) must have a _champion_ who is responsible for addressing feedback and +completing next steps. An RFC may have multiple _champions_. The spec editors +are not responsible for completing RFCs which lack a _champion_ (though an +editor may be a _champion_ for an RFC). +An RFC which does not have a _champion_ may not progress through stages, and can +become stale. Stale proposals may be picked up by a new _champion_ or may be +_rejected_. ## RFC Contribution Stages -RFCs are guided by a *champion* through a series of stages: *strawman*, -*proposal*, *draft*, and *accepted* (or *rejected*), each of which has suggested +RFCs are guided by a _champion_ through a series of stages: _strawman_, +_proposal_, _draft_, and _accepted_ (or _rejected_), each of which has suggested entrance criteria and next steps detailed below. RFCs typically advance one -stage at a time, but may advance multiple stages at a time. Stage -advancements typically occur during -[Working Group](https://github.com/graphql/graphql-wg) meetings, but may also -occur on GitHub. +stage at a time, but may advance multiple stages at a time. Stage advancements +typically occur during [Working Group](https://github.com/graphql/graphql-wg) +meetings, but may also occur on GitHub. In general, it's preferable to start with a pull request so that we can best evaluate the RFC in detail. However, starting with an issue is also permitted if the full details are not worked out. -All RFCs start as either a *strawman* or *proposal*. +All RFCs start as either a _strawman_ or _proposal_. -## Stage 0: *Strawman* +## Stage 0: _Strawman_ -An RFC at the *strawman* stage captures a described problem or -partially-considered solutions. A *strawman* does not need to meet any entrance -criteria. A *strawman's* goal is to prove or disprove a problem and guide -discussion towards either rejection or a preferred solution. A *strawman* may -be an issue or a pull request (though an illustrative pull request is preferrable). +An RFC at the _strawman_ stage captures a described problem or +partially-considered solutions. A _strawman_ does not need to meet any entrance +criteria. A _strawman's_ goal is to prove or disprove a problem and guide +discussion towards either rejection or a preferred solution. A _strawman_ may be +an issue or a pull request (though an illustrative pull request is preferrable). -*There is no entrance criteria for a Strawman* +_There is no entrance criteria for a Strawman_ -As implied by the name [strawman](https://en.wikipedia.org/wiki/Straw_man_proposal), -the goal at this stage is to knock it down (*reject*) by considering other -possible related solutions, showing that the motivating problem can be solved -with no change to the specification, or that it is not aligned with the -*guiding principles*. +As implied by the name +[strawman](https://en.wikipedia.org/wiki/Straw_man_proposal), the goal at this +stage is to knock it down (_reject_) by considering other possible related +solutions, showing that the motivating problem can be solved with no change to +the specification, or that it is not aligned with the _guiding principles_. -Once determined that the *strawman* is compelling, it should seek the entrance -criteria for *proposal*. +Once determined that the _strawman_ is compelling, it should seek the entrance +criteria for _proposal_. +## Stage 1: _Proposal_ -## Stage 1: *Proposal* +An RFC at the _proposal_ stage is a solution to a problem with enough fidelity +to be discussed in detail. It must be backed by a willing _champion_. A +_proposal_'s goal is to make a compelling case for acceptance by describing both +the problem and the solution via examples and spec edits. A _proposal_ should be +a pull request. -An RFC at the *proposal* stage is a solution to a problem with enough fidelity -to be discussed in detail. It must be backed by a willing *champion*. A -*proposal*'s goal is to make a compelling case for acceptance by describing -both the problem and the solution via examples and spec edits. A *proposal* -should be a pull request. +_Entrance criteria:_ -*Entrance criteria:* +- Identified _champion_ +- Clear explanation of problem and solution +- Illustrative examples +- Incomplete spec edits +- Identification of potential concerns, challenges, and drawbacks -* Identified *champion* -* Clear explanation of problem and solution -* Illustrative examples -* Incomplete spec edits -* Identification of potential concerns, challenges, and drawbacks - -A *proposal* is subject to the same discussion as a *strawman*: ensuring that it -is well aligned with the *guiding principles*, is a problem worth solving, and -is the preferred solution to that problem. A *champion* is not expected to have +A _proposal_ is subject to the same discussion as a _strawman_: ensuring that it +is well aligned with the _guiding principles_, is a problem worth solving, and +is the preferred solution to that problem. A _champion_ is not expected to have confidence in every detail at this stage and should instead focus on identifying and resolving issues and edge-cases. To better understand the technical -ramifications of the *proposal*, a *champion* is encouraged to implement it in a +ramifications of the _proposal_, a _champion_ is encouraged to implement it in a GraphQL library. -Most *proposals* are expected to evolve or change and may be rejected. Therefore, -it is unwise to rely on a *proposal* in a production GraphQL service. GraphQL -libraries *may* implement *proposals*, though are encouraged to not enable the -*proposed* feature without explicit opt-in. - +Most _proposals_ are expected to evolve or change and may be rejected. +Therefore, it is unwise to rely on a _proposal_ in a production GraphQL service. +GraphQL libraries _may_ implement _proposals_, though are encouraged to not +enable the _proposed_ feature without explicit opt-in. -## Stage 2: *Draft* +## Stage 2: _Draft_ -An RFC at the *draft* stage is a fully formed solution. There is working group +An RFC at the _draft_ stage is a fully formed solution. There is working group consensus the problem identified should be solved, and this particular solution -is preferred. A *draft's* goal is to precisely and completely describe the -solution and resolve any concerns through library implementations. A *draft* +is preferred. A _draft's_ goal is to precisely and completely describe the +solution and resolve any concerns through library implementations. A _draft_ must be a pull request. -*Entrance criteria:* +_Entrance criteria:_ -* Consensus the solution is preferred (typically via Working Group) -* Resolution of identified concerns and challenges -* Precisely described with spec edits -* Compliant implementation in GraphQL.js (might not be merged) +- Consensus the solution is preferred (typically via Working Group) +- Resolution of identified concerns and challenges +- Precisely described with spec edits +- Compliant implementation in GraphQL.js (might not be merged) -A *proposal* becomes a *draft* when the set of problems or drawbacks have been -fully considered and accepted or resolved, and the solution is deemed -desirable. A *draft*'s goal is to complete final spec edits that are ready to -be merged and implement the *draft* in GraphQL libraries along with tests to -gain confidence that the spec text is sufficient. +A _proposal_ becomes a _draft_ when the set of problems or drawbacks have been +fully considered and accepted or resolved, and the solution is deemed desirable. +A _draft_'s goal is to complete final spec edits that are ready to be merged and +implement the _draft_ in GraphQL libraries along with tests to gain confidence +that the spec text is sufficient. -*Drafts* may continue to evolve and change, occasionally dramatically, and are -not guaranteed to be accepted. Therefore, it is unwise to rely on a *draft* in a -production GraphQL Service. GraphQL libraries *should* implement *drafts* to -provide valuable feedback, though are encouraged not to enable the *draft* +_Drafts_ may continue to evolve and change, occasionally dramatically, and are +not guaranteed to be accepted. Therefore, it is unwise to rely on a _draft_ in a +production GraphQL Service. GraphQL libraries _should_ implement _drafts_ to +provide valuable feedback, though are encouraged not to enable the _draft_ feature without explicit opt-in when possible. +## Stage 3: _Accepted_ -## Stage 3: *Accepted* +An RFC at the _accepted_ stage is a completed solution. According to a spec +editor it is ready to be merged as-is into the spec document. The RFC is ready +to be deployed in GraphQL libraries. An _accepted_ RFC must be implemented in +GraphQL.js. -An RFC at the *accepted* stage is a completed solution. According to a spec -editor it is ready to be merged as-is into the spec document. The RFC is -ready to be deployed in GraphQL libraries. An *accepted* RFC must be -implemented in GraphQL.js. +_Entrance criteria:_ -*Entrance criteria:* +- Consensus the solution is complete (via editor or working group) +- Complete spec edits, including examples and prose +- Compliant implementation in GraphQL.js (fully tested and merged or ready to + merge) -* Consensus the solution is complete (via editor or working group) -* Complete spec edits, including examples and prose -* Compliant implementation in GraphQL.js (fully tested and merged or ready to merge) - -A *draft* is *accepted* when the working group or editor has been convinced via +A _draft_ is _accepted_ when the working group or editor has been convinced via implementations and tests that it appropriately handles all edge cases; that the spec changes not only precisely describe the new syntax and semantics but include sufficient motivating prose and examples; and that the RFC includes -edits to any other affected areas of the spec. Once *accepted*, its *champion* +edits to any other affected areas of the spec. Once _accepted_, its _champion_ should encourage adoption of the RFC by opening issues or pull requests on other popular GraphQL libraries. -An *accepted* RFC is merged into the GraphQL spec's main branch by an editor -and will be included in the next released revision. - +An _accepted_ RFC is merged into the GraphQL spec's main branch by an editor and +will be included in the next released revision. -## Stage X: *Rejected* +## Stage X: _Rejected_ -An RFC may be *rejected* at any point and for any reason. Most rejections occur -when a *strawman* is proven to be unnecessary, is misaligned with the *guiding -principles*, or fails to meet the entrance criteria to become a *proposal*. -A *proposal* may become *rejected* for similar reasons as well as if it fails to -reach consensus or loses the confidence of its *champion*. Likewise a *draft* +An RFC may be _rejected_ at any point and for any reason. Most rejections occur +when a _strawman_ is proven to be unnecessary, is misaligned with the _guiding +principles_, or fails to meet the entrance criteria to become a _proposal_. A +_proposal_ may become _rejected_ for similar reasons as well as if it fails to +reach consensus or loses the confidence of its _champion_. Likewise a _draft_ may encounter unforeseen issues during implementations which cause it to lose -consensus or the confidence of its *champion*. +consensus or the confidence of its _champion_. -RFCs which have lost a *champion* will not be *rejected* immediately, but may -become *rejected* if they fail to attract a new *champion*. +RFCs which have lost a _champion_ will not be _rejected_ immediately, but may +become _rejected_ if they fail to attract a new _champion_. -Once *rejected*, an RFC will typically not be reconsidered. Reconsideration is -possible if a *champion* believes the original reason for rejection no longer +Once _rejected_, an RFC will typically not be reconsidered. Reconsideration is +possible if a _champion_ believes the original reason for rejection no longer applies due to new circumstances or new evidence. diff --git a/README.md b/README.md index 9b6525240..458213632 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,13 @@ GraphQL Logo -The GraphQL specification is edited in the markdown files found in [`/spec`](./spec) -the latest release of which is published at https://graphql.github.io/graphql-spec/. +The GraphQL specification is edited in the markdown files found in +[`/spec`](./spec) the latest release of which is published at +https://graphql.github.io/graphql-spec/. -The latest draft specification can be found at https://graphql.github.io/graphql-spec/draft/ -which tracks the latest commit to the main branch in this repository. +The latest draft specification can be found at +https://graphql.github.io/graphql-spec/draft/ which tracks the latest commit to +the main branch in this repository. Previous releases of the GraphQL specification can be found at permalinks that match their [release tag](https://github.com/graphql/graphql-spec/releases). For @@ -16,17 +18,20 @@ for the particular referenced version. ## Overview -This is a Working Draft of the Specification for GraphQL, a query language for APIs created by Facebook. +This is a Working Draft of the Specification for GraphQL, a query language for +APIs created by Facebook. -The target audience for this specification is not the client developer, but those who have, -or are actively interested in, building their own GraphQL implementations and -tools. +The target audience for this specification is not the client developer, but +those who have, or are actively interested in, building their own GraphQL +implementations and tools. -In order to be broadly adopted, GraphQL will have to target a wide -variety of backend environments, frameworks, and languages, which will necessitate a -collaborative effort across projects and organizations. This specification serves as a point of coordination for this effort. +In order to be broadly adopted, GraphQL will have to target a wide variety of +backend environments, frameworks, and languages, which will necessitate a +collaborative effort across projects and organizations. This specification +serves as a point of coordination for this effort. -Looking for help? Find resources [from the community](https://graphql.org/community/). +Looking for help? Find resources +[from the community](https://graphql.org/community/). ## Getting Started @@ -35,32 +40,31 @@ static validation, and type introspection, each outlined below. To guide you through each of these components, we've written an example designed to illustrate the various pieces of GraphQL. -This example is not comprehensive, but it is designed to quickly introduce -the core concepts of GraphQL, to provide some context before diving into -the more detailed specification or the [GraphQL.js](https://github.com/graphql/graphql-js) -reference implementation. +This example is not comprehensive, but it is designed to quickly introduce the +core concepts of GraphQL, to provide some context before diving into the more +detailed specification or the +[GraphQL.js](https://github.com/graphql/graphql-js) reference implementation. The premise of the example is that we want to use GraphQL to query for -information about characters and locations in the original Star Wars -trilogy. +information about characters and locations in the original Star Wars trilogy. ### Type System -At the heart of any GraphQL implementation is a description of what types -of objects it can return, described in a GraphQL type system and returned -in the GraphQL Schema. +At the heart of any GraphQL implementation is a description of what types of +objects it can return, described in a GraphQL type system and returned in the +GraphQL Schema. For our Star Wars example, the [starWarsSchema.ts](https://github.com/graphql/graphql-js/blob/main/src/__tests__/starWarsSchema.ts) file in GraphQL.js defines this type system. -The most basic type in the system will be `Human`, representing characters -like Luke, Leia, and Han. All humans in our type system will have a name, -so we define the `Human` type to have a field called "name". This returns -a String, and we know that it is not null (since all `Human`s have a name), -so we will define the "name" field to be a non-nullable String. Using a -shorthand notation that we will use throughout the spec and documentation, -we would describe the human type as: +The most basic type in the system will be `Human`, representing characters like +Luke, Leia, and Han. All humans in our type system will have a name, so we +define the `Human` type to have a field called "name". This returns a String, +and we know that it is not null (since all `Human`s have a name), so we will +define the "name" field to be a non-nullable String. Using a shorthand notation +that we will use throughout the spec and documentation, we would describe the +human type as: ```graphql type Human { @@ -68,19 +72,18 @@ type Human { } ``` -This shorthand is convenient for describing the basic shape of a type -system; the JavaScript implementation is more full-featured, and allows types -and fields to be documented. It also sets up the mapping between the -type system and the underlying data; for a test case in GraphQL.js, the -underlying data is a [set of JavaScript objects](https://github.com/graphql/graphql-js/blob/main/src/__tests__/starWarsData.ts), +This shorthand is convenient for describing the basic shape of a type system; +the JavaScript implementation is more full-featured, and allows types and fields +to be documented. It also sets up the mapping between the type system and the +underlying data; for a test case in GraphQL.js, the underlying data is a +[set of JavaScript objects](https://github.com/graphql/graphql-js/blob/main/src/__tests__/starWarsData.ts), but in most cases the backing data will be accessed through some service, and this type system layer will be responsible for mapping from types and fields to that service. -A common pattern in many APIs, and indeed in GraphQL is to give -objects an ID that can be used to refetch the object. So let's add -that to our Human type. We'll also add a string for their home -planet. +A common pattern in many APIs, and indeed in GraphQL is to give objects an ID +that can be used to refetch the object. So let's add that to our Human type. +We'll also add a string for their home planet. ```graphql type Human { @@ -90,16 +93,20 @@ type Human { } ``` -Since we're talking about the Star Wars trilogy, it would be useful -to describe the episodes in which each character appears. To do so, we'll -first define an enum, which lists the three episodes in the trilogy: +Since we're talking about the Star Wars trilogy, it would be useful to describe +the episodes in which each character appears. To do so, we'll first define an +enum, which lists the three episodes in the trilogy: ```graphql -enum Episode { NEWHOPE, EMPIRE, JEDI } +enum Episode { + NEWHOPE + EMPIRE + JEDI +} ``` -Now we want to add a field to `Human` describing what episodes they -were in. This will return a list of `Episode`s: +Now we want to add a field to `Human` describing what episodes they were in. +This will return a list of `Episode`s: ```graphql type Human { @@ -112,7 +119,6 @@ type Human { Now, let's introduce another type, `Droid`: - ```graphql type Droid { id: String @@ -122,20 +128,23 @@ type Droid { } ``` -Now we have two types! Let's add a way of going between them: humans -and droids both have friends. But humans can be friends with both -humans and droids. How do we refer to either a human or a droid? +Now we have two types! Let's add a way of going between them: humans and droids +both have friends. But humans can be friends with both humans and droids. How do +we refer to either a human or a droid? -If we look, we note that there's common functionality between -humans and droids; they both have IDs, names, and episodes in which -they appear. So we'll add an interface, `Character`, and make -both `Human` and `Droid` implement it. Once we have that, we can -add the `friends` field, that returns a list of `Character`s. +If we look, we note that there's common functionality between humans and droids; +they both have IDs, names, and episodes in which they appear. So we'll add an +interface, `Character`, and make both `Human` and `Droid` implement it. Once we +have that, we can add the `friends` field, that returns a list of `Character`s. Our type system so far is: ```graphql -enum Episode { NEWHOPE, EMPIRE, JEDI } +enum Episode { + NEWHOPE + EMPIRE + JEDI +} interface Character { id: String @@ -162,24 +171,27 @@ type Droid implements Character { ``` One question we might ask, though, is whether any of those fields can return -`null`. By default, `null` is a permitted value for any type in GraphQL, -since fetching data to fulfill a GraphQL query often requires talking -to different services that may or may not be available. However, if the -type system can guarantee that a type is never null, then we can mark -it as Non Null in the type system. We indicate that in our shorthand -by adding an "!" after the type. We can update our type system to note -that the `id` is never null. - -Note that while in our current implementation, we can guarantee that more -fields are non-null (since our current implementation has hard-coded data), -we didn't mark them as non-null. One can imagine we would eventually -replace our hardcoded data with a backend service, which might not be -perfectly reliable; by leaving these fields as nullable, we allow -ourselves the flexibility to eventually return null to indicate a backend -error, while also telling the client that the error occurred. +`null`. By default, `null` is a permitted value for any type in GraphQL, since +fetching data to fulfill a GraphQL query often requires talking to different +services that may or may not be available. However, if the type system can +guarantee that a type is never null, then we can mark it as Non Null in the type +system. We indicate that in our shorthand by adding an "!" after the type. We +can update our type system to note that the `id` is never null. + +Note that while in our current implementation, we can guarantee that more fields +are non-null (since our current implementation has hard-coded data), we didn't +mark them as non-null. One can imagine we would eventually replace our hardcoded +data with a backend service, which might not be perfectly reliable; by leaving +these fields as nullable, we allow ourselves the flexibility to eventually +return null to indicate a backend error, while also telling the client that the +error occurred. ```graphql -enum Episode { NEWHOPE, EMPIRE, JEDI } +enum Episode { + NEWHOPE + EMPIRE + JEDI +} interface Character { id: String! @@ -208,9 +220,9 @@ type Droid implements Character { We're missing one last piece: an entry point into the type system. When we define a schema, we define an object type that is the basis for all -query operations. The name of this type is `Query` by convention, and it describes -our public, top-level API. Our `Query` type for this example will look like -this: +query operations. The name of this type is `Query` by convention, and it +describes our public, top-level API. Our `Query` type for this example will look +like this: ```graphql type Query { @@ -220,31 +232,32 @@ type Query { } ``` -In this example, there are three top-level operations -that can be done on our schema: +In this example, there are three top-level operations that can be done on our +schema: - - `hero` returns the `Character` who is the hero of the Star Wars trilogy; it -takes an optional argument that allows us to fetch the hero of a specific -episode instead. - - `human` accepts a non-null string as a query argument, a human's ID, and -returns the human with that ID. - - `droid` does the same for droids. +- `hero` returns the `Character` who is the hero of the Star Wars trilogy; it + takes an optional argument that allows us to fetch the hero of a specific + episode instead. +- `human` accepts a non-null string as a query argument, a human's ID, and + returns the human with that ID. +- `droid` does the same for droids. -These fields demonstrate another feature of the type system, the ability -for a field to specify arguments that configure their behavior. +These fields demonstrate another feature of the type system, the ability for a +field to specify arguments that configure their behavior. -When we package the whole type system together, defining the `Query` type -above as our entry point for queries, this creates a GraphQL Schema. +When we package the whole type system together, defining the `Query` type above +as our entry point for queries, this creates a GraphQL Schema. This example just scratched the surface of the type system. The specification -goes into more detail about this topic in the "Type System" section, and the [type](https://github.com/graphql/graphql-js/blob/main/src/type) -directory in GraphQL.js contains code implementing -a specification-compliant GraphQL type system. +goes into more detail about this topic in the "Type System" section, and the +[type](https://github.com/graphql/graphql-js/blob/main/src/type) directory in +GraphQL.js contains code implementing a specification-compliant GraphQL type +system. ### Query Syntax -GraphQL queries declaratively describe what data the issuer wishes -to fetch from whoever is fulfilling the GraphQL query. +GraphQL queries declaratively describe what data the issuer wishes to fetch from +whoever is fulfilling the GraphQL query. For our Star Wars example, the [starWarsQueryTests.js](https://github.com/graphql/graphql-js/blob/main/src/__tests__/starWarsQuery-test.ts) @@ -264,9 +277,9 @@ query HeroNameQuery { } ``` -The initial line, `query HeroNameQuery`, defines a query with the operation -name `HeroNameQuery` that starts with the schema's root query type; in this -case, `Query`. As defined above, `Query` has a `hero` field that returns a +The initial line, `query HeroNameQuery`, defines a query with the operation name +`HeroNameQuery` that starts with the schema's root query type; in this case, +`Query`. As defined above, `Query` has a `hero` field that returns a `Character`, so we'll query for that. `Character` then has a `name` field that returns a `String`, so we query for that, completing our query. The result of this query would then be: @@ -333,11 +346,10 @@ then we'll get back a response like this: } ``` -One of the key aspects of GraphQL is its ability to nest queries. In the -above query, we asked for R2-D2's friends, but we can ask for more information -about each of those objects. So let's construct a query that asks for R2-D2's -friends, gets their name and episode appearances, then asks for each of *their* -friends. +One of the key aspects of GraphQL is its ability to nest queries. In the above +query, we asked for R2-D2's friends, but we can ask for more information about +each of those objects. So let's construct a query that asks for R2-D2's friends, +gets their name and episode appearances, then asks for each of _their_ friends. ```graphql query NestedQuery { @@ -395,8 +407,8 @@ which will give us the nested response } ``` -The `Query` type above defined a way to fetch a human given their -ID. We can use it by hard-coding the ID in the query: +The `Query` type above defined a way to fetch a human given their ID. We can use +it by hard-coding the ID in the query: ```graphql query FetchLukeQuery { @@ -426,15 +438,14 @@ query FetchSomeIDQuery($someId: String!) { } ``` -This query is now parameterized by `$someId`; to run it, we must provide -that ID. If we ran it with `$someId` set to "1000", we would get Luke; -set to "1002", we would get Han. If we passed an invalid ID here, -we would get `null` back for the `human`, indicating that no such object -exists. +This query is now parameterized by `$someId`; to run it, we must provide that +ID. If we ran it with `$someId` set to "1000", we would get Luke; set to "1002", +we would get Han. If we passed an invalid ID here, we would get `null` back for +the `human`, indicating that no such object exists. -Notice that the key in the response is the name of the field, by default. -It is sometimes useful to change this key, for clarity or to avoid key -collisions when fetching the same field with different arguments. +Notice that the key in the response is the name of the field, by default. It is +sometimes useful to change this key, for clarity or to avoid key collisions when +fetching the same field with different arguments. We can do that with field aliases, as demonstrated in this query: @@ -460,8 +471,8 @@ is: Notice the key is "luke" and not "human", as it was in our previous example where we did not use the alias. -This is particularly useful if we want to use the same field twice -with different arguments, as in the following query: +This is particularly useful if we want to use the same field twice with +different arguments, as in the following query: ```graphql query FetchLukeAndLeiaAliased { @@ -474,8 +485,8 @@ query FetchLukeAndLeiaAliased { } ``` -We aliased the result of the first `human` field to the key -`luke`, and the second to `leia`. So the result will be: +We aliased the result of the first `human` field to the key `luke`, and the +second to `leia`. So the result will be: ```json { @@ -539,13 +550,13 @@ Both of those queries give this result: } ``` -The `UseFragment` and `DuplicateFields` queries will both get the same result, but -`UseFragment` is less verbose; if we wanted to add more fields, we could add +The `UseFragment` and `DuplicateFields` queries will both get the same result, +but `UseFragment` is less verbose; if we wanted to add more fields, we could add it to the common fragment rather than copying it into multiple places. -We defined the type system above, so we know the type of each object -in the output; the query can ask for that type using the special -field `__typename`, defined on every object. +We defined the type system above, so we know the type of each object in the +output; the query can ask for that type using the special field `__typename`, +defined on every object. ```graphql query CheckTypeOfR2 { @@ -595,24 +606,24 @@ As with the type system, this example just scratched the surface of the query language. The specification goes into more detail about this topic in the "Language" section, and the [language](https://github.com/graphql/graphql-js/blob/main/src/language) -directory in GraphQL.js contains code implementing a -specification-compliant GraphQL query language parser and lexer. +directory in GraphQL.js contains code implementing a specification-compliant +GraphQL query language parser and lexer. ### Validation -By using the type system, it can be predetermined whether a GraphQL query -is valid or not. This allows servers and clients to effectively inform -developers when an invalid query has been created, without having to rely -on runtime checks. +By using the type system, it can be predetermined whether a GraphQL query is +valid or not. This allows servers and clients to effectively inform developers +when an invalid query has been created, without having to rely on runtime +checks. For our Star Wars example, the file [starWarsValidationTests.js](https://github.com/graphql/graphql-js/blob/main/src/__tests__/starWarsValidation-test.ts) -contains a number of demonstrations of invalid operations, and is a test -file that can be run to exercise the reference implementation's validator. +contains a number of demonstrations of invalid operations, and is a test file +that can be run to exercise the reference implementation's validator. To start, let's take a complex valid query. This is the `NestedQuery` example -from the above section, but with the duplicated fields factored out into -a fragment: +from the above section, but with the duplicated fields factored out into a +fragment: ```graphql query NestedQueryWithFragment { @@ -635,10 +646,9 @@ fragment NameAndAppearances on Character { And this query is valid. Let's take a look at some invalid queries! -When we query for fields, we have to query for a field that exists on the -given type. So as `hero` returns a `Character`, we have to query for a field -on `Character`. That type does not have a `favoriteSpaceship` field, so this -query: +When we query for fields, we have to query for a field that exists on the given +type. So as `hero` returns a `Character`, we have to query for a field on +`Character`. That type does not have a `favoriteSpaceship` field, so this query: ```graphql # INVALID: favoriteSpaceship does not exist on Character @@ -651,9 +661,9 @@ query HeroSpaceshipQuery { is invalid. -Whenever we query for a field and it returns something other than a scalar -or an enum, we need to specify what data we want to get back from the field. -Hero returns a `Character`, and we've been requesting fields like `name` and +Whenever we query for a field and it returns something other than a scalar or an +enum, we need to specify what data we want to get back from the field. Hero +returns a `Character`, and we've been requesting fields like `name` and `appearsIn` on it; if we omit that, the query will not be valid: ```graphql @@ -663,8 +673,8 @@ query HeroNoFieldsQuery { } ``` -Similarly, if a field is a scalar, it doesn't make sense to query for -additional fields on it, and doing so will make the query invalid: +Similarly, if a field is a scalar, it doesn't make sense to query for additional +fields on it, and doing so will make the query invalid: ```graphql # INVALID: name is a scalar, so fields are not permitted @@ -677,10 +687,10 @@ query HeroFieldsOnScalarQuery { } ``` -Earlier, it was noted that a query can only query for fields on the type -in question; when we query for `hero` which returns a `Character`, we -can only query for fields that exist on `Character`. What happens if we -want to query for R2-D2s primary function, though? +Earlier, it was noted that a query can only query for fields on the type in +question; when we query for `hero` which returns a `Character`, we can only +query for fields that exist on `Character`. What happens if we want to query for +R2-D2s primary function, though? ```graphql # INVALID: primaryFunction does not exist on Character @@ -694,9 +704,9 @@ query DroidFieldOnCharacter { That query is invalid, because `primaryFunction` is not a field on `Character`. We want some way of indicating that we wish to fetch `primaryFunction` if the -`Character` is a `Droid`, and to ignore that field otherwise. We can use -the fragments we introduced earlier to do this. By setting up a fragment defined -on `Droid` and including it, we ensure that we only query for `primaryFunction` +`Character` is a `Droid`, and to ignore that field otherwise. We can use the +fragments we introduced earlier to do this. By setting up a fragment defined on +`Droid` and including it, we ensure that we only query for `primaryFunction` where it is defined. ```graphql @@ -712,11 +722,10 @@ fragment DroidFields on Droid { } ``` -This query is valid, but it's a bit verbose; named fragments were valuable -above when we used them multiple times, but we're only using this one once. -Instead of using a named fragment, we can use an inline fragment; this -still allows us to indicate the type we are querying on, but without naming -a separate fragment: +This query is valid, but it's a bit verbose; named fragments were valuable above +when we used them multiple times, but we're only using this one once. Instead of +using a named fragment, we can use an inline fragment; this still allows us to +indicate the type we are querying on, but without naming a separate fragment: ```graphql query DroidFieldInInlineFragment { @@ -729,19 +738,18 @@ query DroidFieldInInlineFragment { } ``` -This has just scratched the surface of the validation system; there -are a number of validation rules in place to ensure that a GraphQL query -is semantically meaningful. The specification goes into more detail about this -topic in the "Validation" section, and the +This has just scratched the surface of the validation system; there are a number +of validation rules in place to ensure that a GraphQL query is semantically +meaningful. The specification goes into more detail about this topic in the +"Validation" section, and the [validation](https://github.com/graphql/graphql-js/blob/main/src/validation) -directory in GraphQL.js contains code implementing a -specification-compliant GraphQL validator. +directory in GraphQL.js contains code implementing a specification-compliant +GraphQL validator. ### Introspection -It's often useful to ask a GraphQL schema for information about what -queries it supports. GraphQL allows us to do so using the introspection -system! +It's often useful to ask a GraphQL schema for information about what queries it +supports. GraphQL allows us to do so using the introspection system! For our Star Wars example, the file [starWarsIntrospectionTests.js](https://github.com/graphql/graphql-js/blob/main/src/__tests__/starWarsIntrospection-test.ts) @@ -749,10 +757,9 @@ contains a number of queries demonstrating the introspection system, and is a test file that can be run to exercise the reference implementation's introspection system. -We designed the type system, so we know what types are available, but if -we didn't, we can ask GraphQL, by querying the `__schema` field, always -available on the root type of a Query. Let's do so now, and ask what types -are available. +We designed the type system, so we know what types are available, but if we +didn't, we can ask GraphQL, by querying the `__schema` field, always available +on the root type of a Query. Let's do so now, and ask what types are available. ```graphql query IntrospectionTypeQuery { @@ -819,13 +826,13 @@ and we get back: Wow, that's a lot of types! What are they? Let's group them: - - **Query, Character, Human, Episode, Droid** - These are the ones that we -defined in our type system. - - **String, Boolean** - These are built-in scalars that the type system -provided. - - **`__Schema`, `__Type`, `__TypeKind`, `__Field`, `__InputValue`, `__EnumValue`, -`__Directive`** - These all are preceded with a double underscore, indicating -that they are part of the introspection system. +- **Query, Character, Human, Episode, Droid** - These are the ones that we + defined in our type system. +- **String, Boolean** - These are built-in scalars that the type system + provided. +- **`__Schema`, `__Type`, `__TypeKind`, `__Field`, `__InputValue`, + `__EnumValue`, `__Directive`** - These all are preceded with a double + underscore, indicating that they are part of the introspection system. Now, let's try and figure out a good place to start exploring what queries are available. When we designed our type system, we specified what type all queries @@ -853,16 +860,14 @@ and we get back: } ``` -And that matches what we said in the type system section, that -the `Query` type is where we will start! Note that the naming here -was just by convention; we could have named our `Query` type anything -else, and it still would have been returned here if we had specified it -as the starting type for queries. Naming it `Query`, though, is a useful -convention. - -It is often useful to examine one specific type. Let's take a look at -the `Droid` type: +And that matches what we said in the type system section, that the `Query` type +is where we will start! Note that the naming here was just by convention; we +could have named our `Query` type anything else, and it still would have been +returned here if we had specified it as the starting type for queries. Naming it +`Query`, though, is a useful convention. +It is often useful to examine one specific type. Let's take a look at the +`Droid` type: ```graphql query IntrospectionDroidTypeQuery { @@ -882,8 +887,8 @@ and we get back: } ``` -What if we want to know more about Droid, though? For example, is it -an interface or an object? +What if we want to know more about Droid, though? For example, is it an +interface or an object? ```graphql query IntrospectionDroidKindQuery { @@ -905,9 +910,8 @@ and we get back: } ``` -`kind` returns a `__TypeKind` enum, one of whose values is `OBJECT`. If -we asked about `Character` instead: - +`kind` returns a `__TypeKind` enum, one of whose values is `OBJECT`. If we asked +about `Character` instead: ```graphql query IntrospectionCharacterKindQuery { @@ -931,8 +935,8 @@ and we get back: We'd find that it is an interface. -It's useful for an object to know what fields are available, so let's -ask the introspection system about `Droid`: +It's useful for an object to know what fields are available, so let's ask the +introspection system about `Droid`: ```graphql query IntrospectionDroidFieldsQuery { @@ -998,14 +1002,14 @@ and we get back: Those are our fields that we defined on `Droid`! -`id` looks a bit weird there, it has no name for the type. That's -because it's a "wrapper" type of kind `NON_NULL`. If we queried for -`ofType` on that field's type, we would find the `String` type there, -telling us that this is a non-null String. +`id` looks a bit weird there, it has no name for the type. That's because it's a +"wrapper" type of kind `NON_NULL`. If we queried for `ofType` on that field's +type, we would find the `String` type there, telling us that this is a non-null +String. Similarly, both `friends` and `appearsIn` have no name, since they are the -`LIST` wrapper type. We can query for `ofType` on those types, which will -tell us what these are lists of. +`LIST` wrapper type. We can query for `ofType` on those types, which will tell +us what these are lists of. ```graphql query IntrospectionDroidWrappedFieldsQuery { @@ -1087,8 +1091,8 @@ and we get back: } ``` -Let's end with a feature of the introspection system particularly useful -for tooling; let's ask the system for documentation! +Let's end with a feature of the introspection system particularly useful for +tooling; let's ask the system for documentation! ```graphql query IntrospectionDroidDescriptionQuery { @@ -1113,29 +1117,42 @@ yields So we can access the documentation about the type system using introspection, and create documentation browsers, or rich IDE experiences. -This has just scratched the surface of the introspection system; we can -query for enum values, what interfaces a type implements, and more. We -can even introspect on the introspection system itself. The specification goes -into more detail about this topic in the "Introspection" section, and the [introspection](https://github.com/graphql/graphql-js/blob/main/src/type/introspection.ts) -file in GraphQL.js -contains code implementing a specification-compliant GraphQL query -introspection system. +This has just scratched the surface of the introspection system; we can query +for enum values, what interfaces a type implements, and more. We can even +introspect on the introspection system itself. The specification goes into more +detail about this topic in the "Introspection" section, and the +[introspection](https://github.com/graphql/graphql-js/blob/main/src/type/introspection.ts) +file in GraphQL.js contains code implementing a specification-compliant GraphQL +query introspection system. ### Additional Content This README walked through the GraphQL.js reference implementation's type -system, query execution, validation, and introspection systems. There's more -in both [GraphQL.js](https://github.com/graphql/graphql-js/) and specification, +system, query execution, validation, and introspection systems. There's more in +both [GraphQL.js](https://github.com/graphql/graphql-js/) and specification, including a description and implementation for executing queries, how to format a response, explaining how a type system maps to an underlying implementation, and how to format a GraphQL response, as well as the grammar for GraphQL. ### Contributing to this repo -This repository is managed by EasyCLA. Project participants must sign the free ([GraphQL Specification Membership agreement](https://preview-spec-membership.graphql.org) before making a contribution. You only need to do this one time, and it can be signed by [individual contributors](https://individual-spec-membership.graphql.org/) or their [employers](https://corporate-spec-membership.graphql.org/). - -To initiate the signature process please open a PR against this repo. The EasyCLA bot will block the merge if we still need a membership agreement from you. - -You can find [detailed information here](https://github.com/graphql/graphql-wg/tree/main/membership). If you have issues, please email [operations@graphql.org](mailto:operations@graphql.org). - -If your company benefits from GraphQL and you would like to provide essential financial support for the systems and people that power our community, please also consider membership in the [GraphQL Foundation](https://foundation.graphql.org/join). +This repository is managed by EasyCLA. Project participants must sign the free +([GraphQL Specification Membership agreement](https://preview-spec-membership.graphql.org) +before making a contribution. You only need to do this one time, and it can be +signed by +[individual contributors](https://individual-spec-membership.graphql.org/) or +their [employers](https://corporate-spec-membership.graphql.org/). + +To initiate the signature process please open a PR against this repo. The +EasyCLA bot will block the merge if we still need a membership agreement from +you. + +You can find +[detailed information here](https://github.com/graphql/graphql-wg/tree/main/membership). +If you have issues, please email +[operations@graphql.org](mailto:operations@graphql.org). + +If your company benefits from GraphQL and you would like to provide essential +financial support for the systems and people that power our community, please +also consider membership in the +[GraphQL Foundation](https://foundation.graphql.org/join). diff --git a/package-lock.json b/package-lock.json index 69bcb2c33..b4db61e3c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -4,10 +4,12 @@ "requires": true, "packages": { "": { + "name": "graphql-spec", "license": "OWFa-1.0", "devDependencies": { "cspell": "5.9.1", "nodemon": "2.0.12", + "prettier": "^2.3.0", "spec-md": "3.0.2" } }, @@ -1808,6 +1810,18 @@ "node": ">=4" } }, + "node_modules/prettier": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", + "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/prismjs": { "version": "1.24.1", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.24.1.tgz", @@ -3678,6 +3692,12 @@ "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=", "dev": true }, + "prettier": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.5.1.tgz", + "integrity": "sha512-vBZcPRUR5MZJwoyi3ZoyQlc1rXeEck8KgeC9AwwOn+exuxLxq5toTRDTSaVrXHxelDMHy9zlicw8u66yxoSUFg==", + "dev": true + }, "prismjs": { "version": "1.24.1", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.24.1.tgz", diff --git a/package.json b/package.json index 7f05bb67e..45f0b1983 100644 --- a/package.json +++ b/package.json @@ -16,12 +16,19 @@ "test": "npm run test:build && npm run test:spellcheck", "test:build": "spec-md spec/GraphQL.md > /dev/null", "test:spellcheck": "cspell 'spec/**/*.md' README.md", + "format": "prettier --write '**/*.{md,yml,yaml,json}'", + "format:check": "prettier --check '**/*.{md,yml,yaml,json}'", "build": "./build.sh", "watch": "nodemon -e json,md --exec 'npm run build'" }, "devDependencies": { "cspell": "5.9.1", "nodemon": "2.0.12", + "prettier": "^2.3.0", "spec-md": "3.0.2" + }, + "prettier": { + "proseWrap": "always", + "trailingComma": "none" } } diff --git a/signed-agreements/README.md b/signed-agreements/README.md index 790d52f9f..a78649197 100644 --- a/signed-agreements/README.md +++ b/signed-agreements/README.md @@ -3,8 +3,8 @@ The signed agreement found in this directory was contributed when this specification was licensed under OWFa 1.0 on September 26, 2017 by Facebook. -Since then the [GraphQL Foundation](https://graphql.org/foundation/) was -formed in 2019, at which point the GraphQL Specification Project became a +Since then the [GraphQL Foundation](https://graphql.org/foundation/) was formed +in 2019, at which point the GraphQL Specification Project became a [Joint Development Foundation](https://www.jointdevelopment.org/) project. The charter and legal documents currently governing this specification and other diff --git a/spec/Appendix A -- Notation Conventions.md b/spec/Appendix A -- Notation Conventions.md index 8f205a105..2375f3d67 100644 --- a/spec/Appendix A -- Notation Conventions.md +++ b/spec/Appendix A -- Notation Conventions.md @@ -4,9 +4,8 @@ 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. - +This appendix seeks to explain these notations in greater detail to avoid +ambiguity. ## Context-Free Grammar @@ -35,16 +34,17 @@ NonTerminalWithSingleDefinition : NonTerminal `terminal` While using the following notation for a production with a list of definitions: NonTerminalWithManyDefinitions : - - OtherNonTerminal `terminal` - - `terminal` -A definition may refer to itself, which describes repetitive sequences, -for example: +- OtherNonTerminal `terminal` +- `terminal` + +A definition may refer to itself, which describes repetitive sequences, for +example: ListOfLetterA : - - ListOfLetterA `a` - - `a` +- ListOfLetterA `a` +- `a` ## Lexical and Syntactical Grammar @@ -55,11 +55,11 @@ 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 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. Word :: Letter+ @@ -70,7 +70,6 @@ a one colon `:` definition. Sentence : Word+ `.` - ## Grammar Notation This specification uses some additional notation to describe common patterns, @@ -78,7 +77,6 @@ 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 @@ -90,14 +88,13 @@ that the same sequence of characters could not replace {SevenCarlinWords}. SafeWord : Word but not SevenCarlinWords -A grammar may also list a number of restrictions after "but not" separated -by "or". +A grammar may also list a number of restrictions after "but not" separated by +"or". For example: NonBooleanName : Name but not `true` or `false` - **Lookahead Restrictions** A grammar production may specify that certain characters or tokens are not @@ -109,7 +106,6 @@ cannot be followed by yet another {Letter}. Word :: Letter+ [lookahead != Letter] - **Optionality and Lists** A subscript suffix "{Symbol?}" is shorthand for two possible sequences, one @@ -122,8 +118,9 @@ Sentence : Noun Verb Adverb? is shorthand for Sentence : - - Noun Verb Adverb - - Noun Verb + +- Noun Verb Adverb +- Noun Verb A subscript suffix "{Symbol+}" is shorthand for a list of one or more of that symbol, represented as an additional recursive production. @@ -137,43 +134,45 @@ is shorthand for Book : Cover Page_list Cover Page_list : - - Page_list Page - - Page +- Page_list Page +- Page **Parameterized Grammar Productions** -A symbol definition subscript suffix parameter in braces "{Symbol[Param]}" -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]". +A symbol definition subscript suffix parameter in braces "{Symbol[Param]}" 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: Example[Param] : - - A - - B[Param] - - C[?Param] - - [+Param] D - - [~Param] E + +- A +- B[Param] +- C[?Param] +- [+Param] D +- [~Param] E is shorthand for Example : - - A - - B_param - - C - - E + +- A +- B_param +- C +- E Example_param : - - A - - B_param - - C_param - - D +- A +- B_param +- C_param +- D ## Grammar Semantics @@ -184,13 +183,12 @@ For example, this describes how a parser should interpret a string literal: StringValue :: `""` - * Return an empty Unicode character sequence. +- Return an empty Unicode character sequence. StringValue :: `"` StringCharacter+ `"` - * Return the Unicode character sequence of all {StringCharacter} - Unicode character values. - +- Return the Unicode character sequence of all {StringCharacter} Unicode + character values. ## Algorithms @@ -198,21 +196,23 @@ 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. +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: Fibonacci(number): - * If {number} is {0}: - * Return {1}. - * If {number} is {1}: - * Return {2}. - * Let {previousNumber} be {number} - {1}. - * Let {previousPreviousNumber} be {number} - {2}. - * Return {Fibonacci(previousNumber)} + {Fibonacci(previousPreviousNumber)}. - -Note: Algorithms described in this document are written to be easy to understand. -Implementers are encouraged to include equivalent but optimized implementations. + +- If {number} is {0}: + - Return {1}. +- If {number} is {1}: + - Return {2}. +- Let {previousNumber} be {number} - {1}. +- Let {previousPreviousNumber} be {number} - {2}. +- Return {Fibonacci(previousNumber)} + {Fibonacci(previousPreviousNumber)}. + +Note: Algorithms described in this document are written to be easy to +understand. Implementers are encouraged to include equivalent but optimized +implementations. diff --git a/spec/Appendix B -- Grammar Summary.md b/spec/Appendix B -- Grammar Summary.md index 01c900a3f..7afe9e11b 100644 --- a/spec/Appendix B -- Grammar Summary.md +++ b/spec/Appendix B -- Grammar Summary.md @@ -3,85 +3,95 @@ ## Source Text SourceCharacter :: - - "U+0009" - - "U+000A" - - "U+000D" - - "U+0020–U+FFFF" +- "U+0009" +- "U+000A" +- "U+000D" +- "U+0020–U+FFFF" ## Ignored Tokens Ignored :: - - UnicodeBOM - - WhiteSpace - - LineTerminator - - Comment - - Comma + +- UnicodeBOM +- WhiteSpace +- LineTerminator +- Comment +- Comma UnicodeBOM :: "Byte Order Mark (U+FEFF)" WhiteSpace :: - - "Horizontal Tab (U+0009)" - - "Space (U+0020)" + +- "Horizontal Tab (U+0009)" +- "Space (U+0020)" LineTerminator :: - - "New Line (U+000A)" - - "Carriage Return (U+000D)" [lookahead != "New Line (U+000A)"] - - "Carriage Return (U+000D)" "New Line (U+000A)" -Comment :: `#` CommentChar* [lookahead != CommentChar] +- "New Line (U+000A)" +- "Carriage Return (U+000D)" [lookahead != "New Line (U+000A)"] +- "Carriage Return (U+000D)" "New Line (U+000A)" + +Comment :: `#` CommentChar\* [lookahead != CommentChar] CommentChar :: SourceCharacter but not LineTerminator Comma :: , - ## Lexical Tokens Token :: - - Punctuator - - Name - - IntValue - - FloatValue - - StringValue + +- Punctuator +- Name +- IntValue +- FloatValue +- StringValue Punctuator :: one of ! $ & ( ) ... : = @ [ ] { | } Name :: - - NameStart NameContinue* [lookahead != NameContinue] + +- NameStart NameContinue\* [lookahead != NameContinue] NameStart :: - - Letter - - `_` + +- Letter +- `_` NameContinue :: - - Letter - - Digit - - `_` + +- Letter +- Digit +- `_` Letter :: one of - - `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` + +- `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` Digit :: one of - - `0` `1` `2` `3` `4` `5` `6` `7` `8` `9` + +- `0` `1` `2` `3` `4` `5` `6` `7` `8` `9` IntValue :: IntegerPart [lookahead != {Digit, `.`, NameStart}] IntegerPart :: - - NegativeSign? 0 - - NegativeSign? NonZeroDigit Digit* + +- NegativeSign? 0 +- NegativeSign? NonZeroDigit Digit\* NegativeSign :: - NonZeroDigit :: Digit but not `0` FloatValue :: - - IntegerPart FractionalPart ExponentPart [lookahead != {Digit, `.`, NameStart}] - - IntegerPart FractionalPart [lookahead != {Digit, `.`, NameStart}] - - IntegerPart ExponentPart [lookahead != {Digit, `.`, NameStart}] + +- IntegerPart FractionalPart ExponentPart [lookahead != {Digit, `.`, NameStart}] +- IntegerPart FractionalPart [lookahead != {Digit, `.`, NameStart}] +- IntegerPart ExponentPart [lookahead != {Digit, `.`, NameStart}] FractionalPart :: . Digit+ @@ -92,53 +102,59 @@ ExponentIndicator :: one of `e` `E` Sign :: one of + - StringValue :: - - `""` [lookahead != `"`] - - `"` StringCharacter+ `"` - - `"""` BlockStringCharacter* `"""` + +- `""` [lookahead != `"`] +- `"` StringCharacter+ `"` +- `"""` BlockStringCharacter\* `"""` StringCharacter :: - - SourceCharacter but not `"` or `\` or LineTerminator - - `\u` EscapedUnicode - - `\` EscapedCharacter + +- SourceCharacter but not `"` or `\` or LineTerminator +- `\u` EscapedUnicode +- `\` EscapedCharacter EscapedUnicode :: /[0-9A-Fa-f]{4}/ EscapedCharacter :: one of `"` `\` `/` `b` `f` `n` `r` `t` BlockStringCharacter :: - - SourceCharacter but not `"""` or `\"""` - - `\"""` + +- SourceCharacter but not `"""` or `\"""` +- `\"""` Note: Block string values are interpreted to exclude blank initial and trailing lines and uniform indentation with {BlockStringValue()}. - ## Document Syntax Document : Definition+ Definition : - - ExecutableDefinition - - TypeSystemDefinitionOrExtension + +- ExecutableDefinition +- TypeSystemDefinitionOrExtension ExecutableDocument : ExecutableDefinition+ ExecutableDefinition : - - OperationDefinition - - FragmentDefinition + +- OperationDefinition +- FragmentDefinition OperationDefinition : - - OperationType Name? VariableDefinitions? Directives? SelectionSet - - SelectionSet + +- OperationType Name? VariableDefinitions? Directives? SelectionSet +- SelectionSet OperationType : one of `query` `mutation` `subscription` SelectionSet : { Selection+ } Selection : - - Field - - FragmentSpread - - InlineFragment + +- Field +- FragmentSpread +- InlineFragment Field : Alias? Name Arguments? Directives? SelectionSet? @@ -152,22 +168,24 @@ FragmentSpread : ... FragmentName Directives? InlineFragment : ... TypeCondition? Directives? SelectionSet -FragmentDefinition : fragment FragmentName TypeCondition Directives? SelectionSet +FragmentDefinition : fragment FragmentName TypeCondition Directives? +SelectionSet FragmentName : Name but not `on` TypeCondition : on NamedType Value[Const] : - - [~Const] Variable - - IntValue - - FloatValue - - StringValue - - BooleanValue - - NullValue - - EnumValue - - ListValue[?Const] - - ObjectValue[?Const] + +- [~Const] Variable +- IntValue +- FloatValue +- StringValue +- BooleanValue +- NullValue +- EnumValue +- ListValue[?Const] +- ObjectValue[?Const] BooleanValue : one of `true` `false` @@ -176,12 +194,14 @@ NullValue : `null` EnumValue : Name but not `true`, `false` or `null` ListValue[Const] : - - [ ] - - [ Value[?Const]+ ] + +- [ ] +- [ Value[?Const]+ ] ObjectValue[Const] : - - { } - - { ObjectField[?Const]+ } + +- { } +- { ObjectField[?Const]+ } ObjectField[Const] : Name : Value[?Const] @@ -194,17 +214,19 @@ Variable : $ Name DefaultValue : = Value[Const] Type : - - NamedType - - ListType - - NonNullType + +- NamedType +- ListType +- NonNullType NamedType : Name ListType : [ Type ] NonNullType : - - NamedType ! - - ListType ! + +- NamedType ! +- ListType ! Directives[Const] : Directive[?Const]+ @@ -213,142 +235,174 @@ Directive[Const] : @ Name Arguments[?Const]? TypeSystemDocument : TypeSystemDefinition+ TypeSystemDefinition : - - SchemaDefinition - - TypeDefinition - - DirectiveDefinition + +- SchemaDefinition +- TypeDefinition +- DirectiveDefinition TypeSystemExtensionDocument : TypeSystemDefinitionOrExtension+ TypeSystemDefinitionOrExtension : - - TypeSystemDefinition - - TypeSystemExtension + +- TypeSystemDefinition +- TypeSystemExtension TypeSystemExtension : - - SchemaExtension - - TypeExtension -SchemaDefinition : Description? schema Directives[Const]? { RootOperationTypeDefinition+ } +- SchemaExtension +- TypeExtension + +SchemaDefinition : Description? schema Directives[Const]? { +RootOperationTypeDefinition+ } SchemaExtension : - - extend schema Directives[Const]? { RootOperationTypeDefinition+ } - - extend schema Directives[Const] [lookahead != `{`] + +- extend schema Directives[Const]? { RootOperationTypeDefinition+ } +- extend schema Directives[Const] [lookahead != `{`] RootOperationTypeDefinition : OperationType : NamedType Description : StringValue TypeDefinition : - - ScalarTypeDefinition - - ObjectTypeDefinition - - InterfaceTypeDefinition - - UnionTypeDefinition - - EnumTypeDefinition - - InputObjectTypeDefinition + +- ScalarTypeDefinition +- ObjectTypeDefinition +- InterfaceTypeDefinition +- UnionTypeDefinition +- EnumTypeDefinition +- InputObjectTypeDefinition TypeExtension : - - ScalarTypeExtension - - ObjectTypeExtension - - InterfaceTypeExtension - - UnionTypeExtension - - EnumTypeExtension - - InputObjectTypeExtension + +- ScalarTypeExtension +- ObjectTypeExtension +- InterfaceTypeExtension +- UnionTypeExtension +- EnumTypeExtension +- InputObjectTypeExtension ScalarTypeDefinition : Description? scalar Name Directives[Const]? ScalarTypeExtension : - - extend scalar Name Directives[Const] + +- extend scalar Name Directives[Const] ObjectTypeDefinition : - - Description? type Name ImplementsInterfaces? Directives[Const]? FieldsDefinition - - Description? type Name ImplementsInterfaces? Directives[Const]? [lookahead != `{`] + +- Description? type Name ImplementsInterfaces? Directives[Const]? + FieldsDefinition +- Description? type Name ImplementsInterfaces? Directives[Const]? [lookahead != + `{`] ObjectTypeExtension : - - extend type Name ImplementsInterfaces? Directives[Const]? FieldsDefinition - - extend type Name ImplementsInterfaces? Directives[Const] [lookahead != `{`] - - extend type Name ImplementsInterfaces [lookahead != `{`] + +- extend type Name ImplementsInterfaces? Directives[Const]? FieldsDefinition +- extend type Name ImplementsInterfaces? Directives[Const] [lookahead != `{`] +- extend type Name ImplementsInterfaces [lookahead != `{`] ImplementsInterfaces : - - ImplementsInterfaces & NamedType - - implements `&`? NamedType + +- ImplementsInterfaces & NamedType +- implements `&`? NamedType FieldsDefinition : { FieldDefinition+ } -FieldDefinition : Description? Name ArgumentsDefinition? : Type Directives[Const]? +FieldDefinition : Description? Name ArgumentsDefinition? : Type +Directives[Const]? ArgumentsDefinition : ( InputValueDefinition+ ) InputValueDefinition : Description? Name : Type DefaultValue? Directives[Const]? InterfaceTypeDefinition : - - Description? interface Name ImplementsInterfaces? Directives[Const]? FieldsDefinition - - Description? interface Name ImplementsInterfaces? Directives[Const]? [lookahead != `{`] + +- Description? interface Name ImplementsInterfaces? Directives[Const]? + FieldsDefinition +- Description? interface Name ImplementsInterfaces? Directives[Const]? + [lookahead != `{`] InterfaceTypeExtension : - - extend interface Name ImplementsInterfaces? Directives[Const]? FieldsDefinition - - extend interface Name ImplementsInterfaces? Directives[Const] [lookahead != `{`] - - extend interface Name ImplementsInterfaces [lookahead != `{`] -UnionTypeDefinition : Description? union Name Directives[Const]? UnionMemberTypes? +- extend interface Name ImplementsInterfaces? Directives[Const]? + FieldsDefinition +- extend interface Name ImplementsInterfaces? Directives[Const] [lookahead != + `{`] +- extend interface Name ImplementsInterfaces [lookahead != `{`] + +UnionTypeDefinition : Description? union Name Directives[Const]? +UnionMemberTypes? UnionMemberTypes : - - UnionMemberTypes | NamedType - - = `|`? NamedType + +- UnionMemberTypes | NamedType +- = `|`? NamedType UnionTypeExtension : - - extend union Name Directives[Const]? UnionMemberTypes - - extend union Name Directives[Const] + +- extend union Name Directives[Const]? UnionMemberTypes +- extend union Name Directives[Const] EnumTypeDefinition : - - Description? enum Name Directives[Const]? EnumValuesDefinition - - Description? enum Name Directives[Const]? [lookahead != `{`] + +- Description? enum Name Directives[Const]? EnumValuesDefinition +- Description? enum Name Directives[Const]? [lookahead != `{`] EnumValuesDefinition : { EnumValueDefinition+ } EnumValueDefinition : Description? EnumValue Directives[Const]? EnumTypeExtension : - - extend enum Name Directives[Const]? EnumValuesDefinition - - extend enum Name Directives[Const] [lookahead != `{`] + +- extend enum Name Directives[Const]? EnumValuesDefinition +- extend enum Name Directives[Const] [lookahead != `{`] InputObjectTypeDefinition : - - Description? input Name Directives[Const]? InputFieldsDefinition - - Description? input Name Directives[Const]? [lookahead != `{`] + +- Description? input Name Directives[Const]? InputFieldsDefinition +- Description? input Name Directives[Const]? [lookahead != `{`] InputFieldsDefinition : { InputValueDefinition+ } InputObjectTypeExtension : - - extend input Name Directives[Const]? InputFieldsDefinition - - extend input Name Directives[Const] [lookahead != `{`] -DirectiveDefinition : Description? directive @ Name ArgumentsDefinition? `repeatable`? on DirectiveLocations +- extend input Name Directives[Const]? InputFieldsDefinition +- extend input Name Directives[Const] [lookahead != `{`] + +DirectiveDefinition : Description? directive @ Name ArgumentsDefinition? +`repeatable`? on DirectiveLocations DirectiveLocations : - - DirectiveLocations | DirectiveLocation - - `|`? DirectiveLocation + +- DirectiveLocations | DirectiveLocation +- `|`? DirectiveLocation DirectiveLocation : - - ExecutableDirectiveLocation - - TypeSystemDirectiveLocation + +- ExecutableDirectiveLocation +- TypeSystemDirectiveLocation ExecutableDirectiveLocation : one of - - `QUERY` - - `MUTATION` - - `SUBSCRIPTION` - - `FIELD` - - `FRAGMENT_DEFINITION` - - `FRAGMENT_SPREAD` - - `INLINE_FRAGMENT` - - `VARIABLE_DEFINITION` + +- `QUERY` +- `MUTATION` +- `SUBSCRIPTION` +- `FIELD` +- `FRAGMENT_DEFINITION` +- `FRAGMENT_SPREAD` +- `INLINE_FRAGMENT` +- `VARIABLE_DEFINITION` TypeSystemDirectiveLocation : one of - - `SCHEMA` - - `SCALAR` - - `OBJECT` - - `FIELD_DEFINITION` - - `ARGUMENT_DEFINITION` - - `INTERFACE` - - `UNION` - - `ENUM` - - `ENUM_VALUE` - - `INPUT_OBJECT` - - `INPUT_FIELD_DEFINITION` + +- `SCHEMA` +- `SCALAR` +- `OBJECT` +- `FIELD_DEFINITION` +- `ARGUMENT_DEFINITION` +- `INTERFACE` +- `UNION` +- `ENUM` +- `ENUM_VALUE` +- `INPUT_OBJECT` +- `INPUT_FIELD_DEFINITION` diff --git a/spec/GraphQL.md b/spec/GraphQL.md index 6c73f4f93..3a5c55d20 100644 --- a/spec/GraphQL.md +++ b/spec/GraphQL.md @@ -1,6 +1,6 @@ # GraphQL -*Current Working Draft* +_Current Working Draft_ **Introduction** @@ -15,8 +15,8 @@ GraphQL ecosystem, and the also in 2019 as the Joint Development Foundation Projects, LLC, GraphQL Series. If your organization benefits from GraphQL, please consider -[becoming a member](https://graphql.org/foundation/join/#graphql-foundation) -and helping us to sustain the activities that support the health of our neutral +[becoming a member](https://graphql.org/foundation/join/#graphql-foundation) 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 @@ -26,7 +26,6 @@ specification can be found at permalinks that match their working draft release can be found at [https://spec.graphql.org/draft](https://spec.graphql.org/draft). - **Copyright notice** Copyright © 2015-2018, Facebook, Inc. @@ -45,41 +44,40 @@ 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](https://www.jointdevelopment.org/). The current [Working Group](https://github.com/graphql/graphql-wg) charter, which includes -the IP policy governing all working group deliverables (including specifications, -source code, and datasets) may be found at +the IP policy governing all working group deliverables (including +specifications, source code, and datasets) may be found at [https://technical-charter.graphql.org](https://technical-charter.graphql.org). -Currently, the licenses governing GraphQL Specification Project deliverables are: - -| Deliverable | License | -| -------------- | ------------------------------------------------------- | -| Specifications | [Open Web Foundation Agreement 1.0 Mode](http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owfa-1-0) (Patent and Copyright) -| Source code | [MIT License](https://opensource.org/licenses/MIT) -| Data sets | [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/) +Currently, the licenses governing GraphQL Specification Project deliverables +are: +| Deliverable | License | +| -------------- | --------------------------------------------------------------------------------------------------------------------------------------- | +| Specifications | [Open Web Foundation Agreement 1.0 Mode](http://www.openwebfoundation.org/legal/the-owf-1-0-agreements/owfa-1-0) (Patent and Copyright) | +| Source code | [MIT License](https://opensource.org/licenses/MIT) | +| Data sets | [CC0 1.0](https://creativecommons.org/publicdomain/zero/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. +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](https://tools.ietf.org/html/rfc2119). -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. +this document are to be interpreted as described in +[IETF RFC 2119](https://tools.ietf.org/html/rfc2119). 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** @@ -93,24 +91,21 @@ 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](#sec-Appendix-Notation-Conventions) for more details -about the definition of algorithms and other notational conventions used in this -document. +understand. Implementers are encouraged to include equivalent but optimized +implementations. +See [Appendix A](#sec-Appendix-Notation-Conventions) 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. +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: +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 This is an example of a non-normative example. @@ -127,7 +122,6 @@ explicitly in prose (e.g. "Note: ") or are set apart in a note block, like this: Note: This is an example of a non-normative note. - # [Overview](Section%201%20--%20Overview.md) # [Language](Section%202%20--%20Language.md) diff --git a/spec/Section 1 -- Overview.md b/spec/Section 1 -- Overview.md index 46c9b3d9f..cc0c51baf 100644 --- a/spec/Section 1 -- Overview.md +++ b/spec/Section 1 -- Overview.md @@ -30,43 +30,43 @@ 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 +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. +- **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. +- **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. +- **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. +- **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. +- **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 @@ -75,11 +75,11 @@ 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. +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. diff --git a/spec/Section 2 -- Language.md b/spec/Section 2 -- Language.md index eb69f8253..bd5e1c3d5 100644 --- a/spec/Section 2 -- Language.md +++ b/spec/Section 2 -- Language.md @@ -1,9 +1,9 @@ # Language -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. +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 @@ -29,32 +29,32 @@ 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 +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 {[lookahead != 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. +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. Note: This typically has the same behavior as a "[maximal munch](https://en.wikipedia.org/wiki/Maximal_munch)" longest possible match, however some lookahead restrictions include additional constraints. - ## Source Text SourceCharacter :: - - "U+0009" - - "U+000A" - - "U+000D" - - "U+0020–U+FFFF" + +- "U+0009" +- "U+000A" +- "U+000D" +- "U+0020–U+FFFF" GraphQL documents are expressed as a sequence of [Unicode](https://unicode.org/standard/standard.html) code points (informally -referred to as *"characters"* through most of this specification). However, with +referred to as _"characters"_ through most of this specification). However, with few exceptions, most of GraphQL is expressed only in the original non-control ASCII range so as to be as widely compatible with as many existing tools, languages, and serialization formats as possible and avoid display issues in @@ -63,40 +63,39 @@ text editors and source control. Note: Non-ASCII Unicode characters may appear freely within {StringValue} and {Comment} portions of GraphQL. - ### Unicode UnicodeBOM :: "Byte Order Mark (U+FEFF)" -The "Byte Order Mark" is a special Unicode character which -may appear at the beginning of a file containing Unicode which programs may use -to determine the fact that the text stream is Unicode, what endianness the text -stream is in, and which of several Unicode encodings to interpret. - +The "Byte Order Mark" is a special Unicode character which may appear at the +beginning of a file containing Unicode which programs may use to determine the +fact that the text stream is Unicode, what endianness the text stream is in, and +which of several Unicode encodings to interpret. ### White Space WhiteSpace :: - - "Horizontal Tab (U+0009)" - - "Space (U+0020)" + +- "Horizontal Tab (U+0009)" +- "Space (U+0020)" 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. +a GraphQL Document, however white space characters may appear within a {String} +or {Comment} token. Note: GraphQL intentionally does not consider Unicode "Zs" category characters -as white-space, avoiding misinterpretation by text editors and source -control tools. - +as white-space, avoiding misinterpretation by text editors and source control +tools. ### Line Terminators LineTerminator :: - - "New Line (U+000A)" - - "Carriage Return (U+000D)" [lookahead != "New Line (U+000A)"] - - "Carriage Return (U+000D)" "New Line (U+000A)" + +- "New Line (U+000A)" +- "Carriage Return (U+000D)" [lookahead != "New Line (U+000A)"] +- "Carriage Return (U+000D)" "New Line (U+000A)" 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 @@ -107,10 +106,9 @@ Note: Any error reporting which provides the line number in the source of the offending syntax should use the preceding amount of {LineTerminator} to produce the line number. - ### Comments -Comment :: `#` CommentChar* [lookahead != CommentChar] +Comment :: `#` CommentChar\* [lookahead != CommentChar] CommentChar :: SourceCharacter but not LineTerminator @@ -119,14 +117,13 @@ GraphQL source documents may contain single-line comments, starting with the A comment can contain any Unicode code point in {SourceCharacter} except {LineTerminator} so a comment always consists of all code points starting with -the {`#`} character up to but not including the {LineTerminator} (or end of -the source). +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. - ### Insignificant Commas Comma :: , @@ -141,15 +138,15 @@ 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. - ### Lexical Tokens Token :: - - Punctuator - - Name - - IntValue - - FloatValue - - StringValue + +- Punctuator +- Name +- IntValue +- FloatValue +- StringValue A GraphQL document is comprised of several kinds of indivisible lexical tokens defined here in a lexical grammar by patterns of source Unicode characters. @@ -157,15 +154,15 @@ Lexical tokens may be separated by {Ignored} tokens. Tokens are later used as terminal symbols in GraphQL syntactic grammar rules. - ### Ignored Tokens Ignored :: - - UnicodeBOM - - WhiteSpace - - LineTerminator - - Comment - - Comma + +- UnicodeBOM +- WhiteSpace +- LineTerminator +- Comment +- Comma {Ignored} tokens are used to improve readability and provide separation between lexical tokens, but are otherwise insignificant and not referenced in @@ -175,41 +172,44 @@ 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 +No {Ignored} may appear _within_ a {Token}, for example no white space characters are permitted between the characters defining a {FloatValue}. - ### Punctuators Punctuator :: one of ! $ & ( ) ... : = @ [ ] { | } -GraphQL documents include punctuation in order to describe structure. GraphQL -is a data description language and not a programming language, therefore GraphQL +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. - ### Names Name :: - - NameStart NameContinue* [lookahead != NameContinue] + +- NameStart NameContinue\* [lookahead != NameContinue] NameStart :: - - Letter - - `_` + +- Letter +- `_` NameContinue :: - - Letter - - Digit - - `_` + +- Letter +- Digit +- `_` Letter :: one of - - `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` + +- `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` Digit :: one of - - `0` `1` `2` `3` `4` `5` `6` `7` `8` `9` + +- `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 @@ -221,7 +221,8 @@ all refer to different names. Underscores are significant, which means 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`}. +{`a1`} cannot be interpreted as two tokens since {`a`} is followed by the +{NameContinue} {`1`}. Note: Names in GraphQL are limited to the Latin ASCII subset of {SourceCharacter} in order to support interoperation with as many other @@ -230,36 +231,37 @@ systems as possible. **Reserved Names** Any {Name} within a GraphQL type system must not start with two underscores -{"__"} unless it is part of the [introspection system](#sec-Introspection) as +{"\_\_"} unless it is part of the [introspection system](#sec-Introspection) as defined by this specification. - ## Document Document : Definition+ Definition : - - ExecutableDefinition - - TypeSystemDefinitionOrExtension + +- ExecutableDefinition +- TypeSystemDefinitionOrExtension ExecutableDocument : ExecutableDefinition+ ExecutableDefinition : - - OperationDefinition - - FragmentDefinition -A GraphQL Document describes a complete file or request string operated on -by a GraphQL service or client. A document contains multiple definitions, either +- OperationDefinition +- FragmentDefinition + +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. +{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}. +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 @@ -274,21 +276,21 @@ 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. - ## Operations OperationDefinition : - - OperationType Name? VariableDefinitions? Directives? SelectionSet - - SelectionSet + +- OperationType Name? VariableDefinitions? Directives? SelectionSet +- SelectionSet OperationType : one of `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. +- 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. @@ -322,19 +324,19 @@ For example, this unnamed query operation is written via query shorthand. Note: many examples below will use the query short-hand syntax. - ## Selection Sets SelectionSet : { Selection+ } Selection : - - Field - - FragmentSpread - - InlineFragment + +- Field +- FragmentSpread +- InlineFragment 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. +that information and nothing more, avoiding over-fetching and under-fetching +data. ```graphql example { @@ -347,7 +349,6 @@ under-fetching data. In this query operation, the `id`, `firstName`, and `lastName` fields form a selection set. Selection sets may also contain fragment references. - ## Fields Field : Alias? Name Arguments? Directives? SelectionSet? @@ -358,8 +359,8 @@ 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. +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. @@ -404,7 +405,6 @@ unique identifier. } ``` - ## Arguments Arguments[Const] : ( Argument[?Const]+ ) @@ -442,8 +442,8 @@ Many arguments can exist for a given field: **Arguments are unordered** -Arguments may be provided in any syntactic order and maintain identical -semantic meaning. +Arguments may be provided in any syntactic order and maintain identical semantic +meaning. These two operations are semantically identical: @@ -459,7 +459,6 @@ These two operations are semantically identical: } ``` - ## Field Alias Alias : Name : @@ -516,12 +515,12 @@ which returns the result: } ``` - ## Fragments FragmentSpread : ... FragmentName Directives? -FragmentDefinition : fragment FragmentName TypeCondition Directives? SelectionSet +FragmentDefinition : fragment FragmentName TypeCondition Directives? +SelectionSet FragmentName : Name but not `on` @@ -552,8 +551,8 @@ query noFragments { } ``` -The repeated fields could be extracted into a fragment and composed by -a parent fragment or operation. +The repeated fields could be extracted into a fragment and composed by a parent +fragment or operation. ```graphql example query withFragments { @@ -574,10 +573,9 @@ fragment friendFields on User { } ``` -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. +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: @@ -607,7 +605,6 @@ fragment standardProfilePic on User { The operations `noFragments`, `withFragments`, and `withNestedFragments` all produce the same response object. - ### Type Conditions TypeCondition : on NamedType @@ -620,8 +617,8 @@ 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. +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: @@ -647,10 +644,10 @@ fragment pageFragment on Page { } ``` -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. +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. ```json example { @@ -667,7 +664,6 @@ will be present and `friends` will not. } ``` - ### Inline Fragments InlineFragment : ... TypeCondition? Directives? SelectionSet @@ -695,9 +691,9 @@ query inlineFragmentTyping { } ``` -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. +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. ```graphql example query inlineFragmentNoType($expandedInfo: Boolean) { @@ -713,34 +709,35 @@ query inlineFragmentNoType($expandedInfo: Boolean) { } ``` - ## Input Values Value[Const] : - - [~Const] Variable - - IntValue - - FloatValue - - StringValue - - BooleanValue - - NullValue - - EnumValue - - ListValue[?Const] - - ObjectValue[?Const] + +- [~Const] Variable +- IntValue +- FloatValue +- StringValue +- BooleanValue +- NullValue +- EnumValue +- ListValue[?Const] +- ObjectValue[?Const] 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). - +specified as a variable. List and inputs objects may also contain variables +(unless defined to be constant). ### Int Value IntValue :: IntegerPart [lookahead != {Digit, `.`, NameStart}] IntegerPart :: - - NegativeSign? 0 - - NegativeSign? NonZeroDigit Digit* + +- NegativeSign? 0 +- NegativeSign? NonZeroDigit Digit\* NegativeSign :: - @@ -750,23 +747,23 @@ 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. +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. - ### Float Value FloatValue :: - - IntegerPart FractionalPart ExponentPart [lookahead != {Digit, `.`, NameStart}] - - IntegerPart FractionalPart [lookahead != {Digit, `.`, NameStart}] - - IntegerPart ExponentPart [lookahead != {Digit, `.`, NameStart}] + +- IntegerPart FractionalPart ExponentPart [lookahead != {Digit, `.`, NameStart}] +- IntegerPart FractionalPart [lookahead != {Digit, `.`, NameStart}] +- IntegerPart ExponentPart [lookahead != {Digit, `.`, NameStart}] FractionalPart :: . Digit+ @@ -776,9 +773,9 @@ ExponentIndicator :: one of `e` `E` Sign :: one of + - -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} 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 @@ -792,9 +789,8 @@ A {FloatValue} must not be followed by a {NameStart}. For example the sequence `0x1.2p3` has no valid lexical representation. Note: The numeric literals {IntValue} and {FloatValue} both restrict being -immediately followed by a letter (or other {NameStart}) to reduce confusion -or unexpected behavior since GraphQL only supports decimal numbers. - +immediately followed by a letter (or other {NameStart}) to reduce confusion or +unexpected behavior since GraphQL only supports decimal numbers. ### Boolean Value @@ -802,29 +798,31 @@ BooleanValue : one of `true` `false` The two keywords `true` and `false` represent the two boolean values. - ### String Value StringValue :: - - `""` [lookahead != `"`] - - `"` StringCharacter+ `"` - - `"""` BlockStringCharacter* `"""` + +- `""` [lookahead != `"`] +- `"` StringCharacter+ `"` +- `"""` BlockStringCharacter\* `"""` StringCharacter :: - - SourceCharacter but not `"` or `\` or LineTerminator - - `\u` EscapedUnicode - - `\` EscapedCharacter + +- SourceCharacter but not `"` or `\` or LineTerminator +- `\u` EscapedUnicode +- `\` EscapedCharacter EscapedUnicode :: /[0-9A-Fa-f]{4}/ EscapedCharacter :: one of `"` `\` `/` `b` `f` `n` `r` `t` BlockStringCharacter :: - - SourceCharacter but not `"""` or `\"""` - - `\"""` -Strings are sequences of characters wrapped in quotation marks (U+0022). -(ex. {`"Hello World"`}). White space and other otherwise-ignored characters are +- SourceCharacter but not `"""` or `\"""` +- `\"""` + +Strings are sequences of characters wrapped in quotation marks (U+0022). (ex. +{`"Hello World"`}). White space and other otherwise-ignored characters are significant within a string value. The empty string {`""`} must not be followed by another {`"`} otherwise it would @@ -832,21 +830,21 @@ 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. -Non-ASCII Unicode characters are allowed within single-quoted strings. -Since {SourceCharacter} must not contain some ASCII control characters, escape +Non-ASCII Unicode characters are allowed within single-quoted strings. Since +{SourceCharacter} must not contain some ASCII control characters, escape sequences must be used to represent these characters. The {`\`}, {`"`} characters also must be escaped. All other escape sequences are optional. **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 +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()}. +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: @@ -888,33 +886,33 @@ which makes it a little harder to read.""" ``` Note: If non-printable ASCII characters are needed in a string value, a standard -quoted string with appropriate escape sequences must be used instead of a -block string. +quoted string with appropriate escape sequences must be used instead of a block +string. **Semantics** StringValue :: `""` - * Return an empty sequence. +- Return an empty sequence. StringValue :: `"` StringCharacter+ `"` - * Return the sequence of all {StringCharacter} code points. +- Return the sequence of all {StringCharacter} code points. StringCharacter :: SourceCharacter but not `"` or `\` or LineTerminator - * Return the code point {SourceCharacter}. +- Return the code point {SourceCharacter}. StringCharacter :: `\u` EscapedUnicode - * Let {value} be the 16-bit hexadecimal value represented by the sequence of - hexadecimal digits within {EscapedUnicode}. - * Return the code point {value}. +- Let {value} be the 16-bit hexadecimal value represented by the sequence of + hexadecimal digits within {EscapedUnicode}. +- Return the code point {value}. StringCharacter :: `\` EscapedCharacter - * Return the code point represented by {EscapedCharacter} according to the - table below. +- Return the code point represented by {EscapedCharacter} according to the table + below. | Escaped Character | Code Point | Character Name | | ----------------- | ---------- | ---------------------------- | @@ -927,50 +925,48 @@ StringCharacter :: `\` EscapedCharacter | {`r`} | U+000D | carriage return | | {`t`} | U+0009 | horizontal tab | -StringValue :: `"""` BlockStringCharacter* `"""` +StringValue :: `"""` BlockStringCharacter\* `"""` - * Let {rawValue} be the Unicode character sequence of all - {BlockStringCharacter} Unicode character values (which may be an empty - sequence). - * Return the result of {BlockStringValue(rawValue)}. +- Let {rawValue} be the Unicode character sequence of all {BlockStringCharacter} + Unicode character values (which may be an empty sequence). +- Return the result of {BlockStringValue(rawValue)}. BlockStringCharacter :: SourceCharacter but not `"""` or `\"""` - * Return the character value of {SourceCharacter}. +- Return the character value of {SourceCharacter}. BlockStringCharacter :: `\"""` - * Return the character sequence `"""`. +- Return the character sequence `"""`. 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 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}. - * 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}. - * Return {formatted}. - +- 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 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}. +- 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}. +- Return {formatted}. ### Null Value @@ -980,8 +976,8 @@ 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. +- 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: @@ -999,9 +995,8 @@ field vs not altering a field, respectively. Neither form may be used for an input expecting a Non-Null type. Note: The same two methods of representing the lack of a value are possible via -variables by either providing the variable value as {null} or not providing -a variable value at all. - +variables by either providing the variable value as {null} or not providing a +variable value at all. ### Enum Value @@ -1012,39 +1007,39 @@ 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. - ### List Value ListValue[Const] : - - [ ] - - [ Value[?Const]+ ] + +- [ ] +- [ Value[?Const]+ ] 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. +Commas are optional throughout GraphQL so trailing commas are allowed and +repeated commas do not represent missing values. **Semantics** ListValue : [ ] - * Return a new empty list value. +- Return a new empty list value. ListValue : [ Value+ ] - * Let {inputList} be a new empty list value. - * For each {Value+} - * Let {value} be the result of evaluating {Value}. - * Append {value} to {inputList}. - * Return {inputList} - +- Let {inputList} be a new empty list value. +- For each {Value+} + - Let {value} be the result of evaluating {Value}. + - Append {value} to {inputList}. +- Return {inputList} ### Input Object Values ObjectValue[Const] : - - { } - - { ObjectField[?Const]+ } + +- { } +- { ObjectField[?Const]+ } ObjectField[Const] : Name : Value[?Const] @@ -1076,17 +1071,16 @@ These two operations are semantically identical: ObjectValue : { } - * Return a new input object value with no fields. +- Return a new input object value with no fields. ObjectValue : { ObjectField+ } - * Let {inputObject} be a new input object value with no fields. - * For each {field} in {ObjectField+} - * Let {name} be {Name} in {field}. - * Let {value} be the result of evaluating {Value} in {field}. - * Add a field to {inputObject} of name {name} containing value {value}. - * Return {inputObject} - +- Let {inputObject} be a new input object value with no fields. +- For each {field} in {ObjectField+} + - Let {name} be {Name} in {field}. + - Let {value} be the result of evaluating {Value} in {field}. + - Add a field to {inputObject} of name {name} containing value {value}. +- Return {inputObject} ## Variables @@ -1098,19 +1092,19 @@ VariableDefinition : Variable : Type DefaultValue? Directives[Const]? DefaultValue : = Value[Const] -A GraphQL operation can be parameterized with variables, maximizing reuse, -and avoiding costly string building in clients at runtime. +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. +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: +In this example, we want to fetch a profile picture size based on the size of a +particular device: ```graphql example query getZuckProfile($devicePicSize: Int) { @@ -1133,52 +1127,54 @@ size `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 +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](#sec-All-Variable-Uses-Defined)). - +that variable, that operation is invalid (see +[All Variable Uses Defined](#sec-All-Variable-Uses-Defined)). ## Type References Type : - - NamedType - - ListType - - NonNullType + +- NamedType +- ListType +- NonNullType NamedType : Name ListType : [ Type ] NonNullType : - - NamedType ! - - ListType ! -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. +- NamedType ! +- ListType ! + +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** Type : Name - * 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} +- 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} Type : [ Type ] - * Let {itemType} be the result of evaluating {Type} - * Let {type} be a List type where {itemType} is the contained type. - * Return {type} +- Let {itemType} be the result of evaluating {Type} +- Let {type} be a List type where {itemType} is the contained type. +- Return {type} Type : Type ! - * Let {nullableType} be the result of evaluating {Type} - * Let {type} be a Non-Null type where {nullableType} is the contained type. - * Return {type} - +- Let {nullableType} be the result of evaluating {Type} +- Let {type} be a Non-Null type where {nullableType} is the contained type. +- Return {type} ## Directives @@ -1189,23 +1185,25 @@ Directive[Const] : @ Name Arguments[?Const]? 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. +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 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. +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. +Directives may be provided in a specific syntactic order which may have semantic +interpretation. These two type definitions may have different semantic meaning: diff --git a/spec/Section 3 -- Type System.md b/spec/Section 3 -- Type System.md index c0cd18f72..b539b936e 100644 --- a/spec/Section 3 -- Type System.md +++ b/spec/Section 3 -- Type System.md @@ -8,9 +8,10 @@ values provided at request time are valid. TypeSystemDocument : TypeSystemDefinition+ TypeSystemDefinition : - - SchemaDefinition - - TypeDefinition - - DirectiveDefinition + +- SchemaDefinition +- TypeDefinition +- DirectiveDefinition The GraphQL language includes an [IDL](https://en.wikipedia.org/wiki/Interface_description_language) used to @@ -26,18 +27,19 @@ only allow {TypeSystemDocument} and not allow {ExecutableDefinition} or Note: The type system definition language is used throughout the remainder of this specification document when illustrating example type systems. - ## Type System Extensions TypeSystemExtensionDocument : TypeSystemDefinitionOrExtension+ TypeSystemDefinitionOrExtension : - - TypeSystemDefinition - - TypeSystemExtension + +- TypeSystemDefinition +- TypeSystemExtension TypeSystemExtension : - - SchemaExtension - - TypeExtension + +- SchemaExtension +- TypeExtension 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 @@ -48,13 +50,12 @@ 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. - ## Descriptions Description : StringValue -Documentation is a first-class feature of GraphQL type systems. To ensure -the documentation of a GraphQL service remains consistent with its capabilities, +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. @@ -112,21 +113,21 @@ enum Language { } ``` - ## Schema -SchemaDefinition : Description? schema Directives[Const]? { RootOperationTypeDefinition+ } +SchemaDefinition : Description? schema Directives[Const]? { +RootOperationTypeDefinition+ } RootOperationTypeDefinition : OperationType : NamedType A GraphQL service's collective type system capabilities are referred to as that service's "schema". A schema is defined in terms of the types and directives it -supports as well as the root operation types for each kind of operation: -query, mutation, and subscription; this determines the place in the type system -where those operations begin. +supports as well as the root operation types 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. +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 @@ -135,7 +136,7 @@ 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 +begins with {"\_\_"} (two underscores), as this is used exclusively by GraphQL's introspection system. ### Root Operation Types @@ -147,8 +148,8 @@ type system where those operations begin. The {`query`} root operation type must be provided and must be an Object type. The {`mutation`} root operation type is optional; if it is not provided, the -service does not support mutations. If it is provided, it must be an -Object type. +service does not support mutations. If it is provided, it must be an Object +type. 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 @@ -190,8 +191,8 @@ mutation { When using the type system definition language, a document must include at most one {`schema`} definition. -In this example, a GraphQL schema is defined with both query and mutation -root operation types: +In this example, a GraphQL schema is defined with both query and mutation root +operation types: ```graphql example schema { @@ -232,12 +233,13 @@ type Query { ### Schema Extension SchemaExtension : - - extend schema Directives[Const]? { RootOperationTypeDefinition+ } - - extend schema Directives[Const] [lookahead != `{`] -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. +- extend schema Directives[Const]? { RootOperationTypeDefinition+ } +- extend schema Directives[Const] [lookahead != `{`] + +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. Note: Schema extensions without additional operation type definitions must not be followed by a {`{`} (such as a query shorthand) to avoid parsing ambiguity. @@ -248,47 +250,46 @@ The same limitation applies to the type definitions and extensions below. Schema extensions have the potential to be invalid if incorrectly defined. 1. The Schema must already be defined. -2. Any non-repeatable directives provided must not already apply to the - original Schema. - +2. Any non-repeatable directives provided must not already apply to the original + Schema. ## Types TypeDefinition : - - ScalarTypeDefinition - - ObjectTypeDefinition - - InterfaceTypeDefinition - - UnionTypeDefinition - - EnumTypeDefinition - - InputObjectTypeDefinition - -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 + +- ScalarTypeDefinition +- ObjectTypeDefinition +- InterfaceTypeDefinition +- UnionTypeDefinition +- EnumTypeDefinition +- InputObjectTypeDefinition + +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. +`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. +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. - +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. ### Wrapping Types @@ -306,50 +307,50 @@ 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. - ### Input 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, +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. +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. IsInputType(type) : - * 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 IsInputType({unwrappedType}) +- If {type} is a Scalar, Enum, or Input Object type: + - Return {true} +- Return {false} IsOutputType(type) : - * 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} +- 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} ### Type Extensions TypeExtension : - - ScalarTypeExtension - - ObjectTypeExtension - - InterfaceTypeExtension - - UnionTypeExtension - - EnumTypeExtension - - InputObjectTypeExtension + +- ScalarTypeExtension +- ObjectTypeExtension +- InterfaceTypeExtension +- UnionTypeExtension +- EnumTypeExtension +- InputObjectTypeExtension 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. - ## Scalars ScalarTypeDefinition : Description? scalar Name Directives[Const]? @@ -369,8 +370,8 @@ GraphQL specifies a basic set of well-defined Scalar types: {Int}, {Float}, 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. +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 @@ -384,20 +385,21 @@ all built-in scalars must be omitted for brevity. 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](https://tools.ietf.org/html/rfc4122). -When querying a field of type `UUID`, you can then rely on the ability to parse -the result with a RFC 4122 compliant parser. Another example of a potentially -useful custom scalar is `URL`, which serializes as a string, but is guaranteed -by the server to be a valid URL. - -:: When defining a custom scalar, GraphQL services should provide a *scalar -specification URL* via the `@specifiedBy` directive or the `specifiedByURL` +which, while serialized as a string, conforms to +[RFC 4122](https://tools.ietf.org/html/rfc4122). When querying a field of type +`UUID`, you can then rely on the ability to parse the result with a RFC 4122 +compliant parser. Another example of a potentially useful custom scalar is +`URL`, which serializes as a string, but is guaranteed by the server to be a +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 +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. ```graphql example @@ -413,7 +415,7 @@ Custom *scalar specification URL*s should not be changed once defined. Doing so would likely disrupt tooling or could introduce breaking changes within the linked specification's contents. -Built-in scalar types must not provide a *scalar specification URL* as they are +Built-in scalar types must not provide a _scalar specification URL_ as they are specified by this document. Note: Custom scalars should also summarize the specified format and provide @@ -421,10 +423,10 @@ examples in their description. **Result Coercion and Serialization** -A GraphQL service, when preparing a field of a given scalar type, must uphold the -contract the scalar type describes, either by coercing the value or producing a -[field error](#sec-Errors.Field-errors) if a value cannot be coerced or if -coercion may result in data loss. +A GraphQL service, when preparing a field of a given scalar type, must uphold +the contract the scalar type describes, either by coercing the value or +producing a [field error](#sec-Errors.Field-errors) if a value cannot be coerced +or if coercion may result in data loss. 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 @@ -432,8 +434,8 @@ expected return type. For example when coercing a field of type {Int} a boolean {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 +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. @@ -446,34 +448,33 @@ 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](#sec-Errors.Request-errors) must be -raised (input values are validated before execution begins). +If a GraphQL service expects a scalar type as input to an argument, coercion is +observable and the rules must be well defined. If an input value does not match +a coercion rule, a [request error](#sec-Errors.Request-errors) must be raised +(input values are validated before execution begins). 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. +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}. - ### Int 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. +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. +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. @@ -494,9 +495,8 @@ value less than -231 or greater than or equal to 231, a request error should be raised. Note: Numeric integer values larger than 32-bit should either use String or a -custom-defined Scalar type, as not all platforms and transports support -encoding integer numbers larger than 32-bit. - +custom-defined Scalar type, as not all platforms and transports support encoding +integer numbers larger than 32-bit. ### Float @@ -522,21 +522,20 @@ coerced to {Float} and must raise a field error. 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. - +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. ### String 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. +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** @@ -553,7 +552,6 @@ 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. - ### Boolean The Boolean scalar type represents `true` or `false`. Response formats should @@ -573,13 +571,12 @@ this may include returning `true` for non-zero numbers. 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. - ### ID The ID scalar type represents a unique identifier, often used to refetch an -object or as the key for a cache. The ID type is serialized in the same way as -a {String}; however, it is not intended to be human-readable. While it is -often numeric, it should always serialize as a {String}. +object or as the key for a cache. The ID type is serialized in the same way as a +{String}; however, it is not intended to be human-readable. While it is often +numeric, it should always serialize as a {String}. **Result Coercion** @@ -599,11 +596,11 @@ 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. - ### Scalar Extensions ScalarTypeExtension : - - extend scalar Name Directives[Const] + +- extend scalar Name Directives[Const] 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 @@ -614,23 +611,27 @@ GraphQL tool or service which adds directives to an existing scalar. Scalar type extensions have the potential to be invalid if incorrectly defined. 1. The named type must already be defined and must be a Scalar type. -2. Any non-repeatable directives provided must not already apply to the - original Scalar type. - +2. Any non-repeatable directives provided must not already apply to the original + Scalar type. ## Objects ObjectTypeDefinition : - - Description? type Name ImplementsInterfaces? Directives[Const]? FieldsDefinition - - Description? type Name ImplementsInterfaces? Directives[Const]? [lookahead != `{`] + +- Description? type Name ImplementsInterfaces? Directives[Const]? + FieldsDefinition +- Description? type Name ImplementsInterfaces? Directives[Const]? [lookahead != + `{`] ImplementsInterfaces : - - ImplementsInterfaces & NamedType - - implements `&`? NamedType + +- ImplementsInterfaces & NamedType +- implements `&`? NamedType FieldsDefinition : { FieldDefinition+ } -FieldDefinition : Description? Name ArgumentsDefinition? : Type Directives[Const]? +FieldDefinition : Description? Name ArgumentsDefinition? : Type +Directives[Const]? GraphQL operations are hierarchical and composed, describing a tree of information. While Scalar types describe the leaf values of these hierarchical @@ -638,12 +639,12 @@ 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. +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 +{"\_\_"} (two underscores), as this is used exclusively by GraphQL's introspection system. For example, a type `Person` could be described as: @@ -657,14 +658,14 @@ type Person { ``` 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. +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. +Only fields that are declared on the object type may validly be queried on that +object. For example, selecting all the fields of `Person`: @@ -704,8 +705,8 @@ Must only yield exactly that subset: } ``` -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 +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`: @@ -755,20 +756,20 @@ And will yield the subset of each object type queried: 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. This ordering -is correctly produced when using the {CollectFields()} algorithm. +excluding fragments for which the type does not apply and fields or fragments +that are skipped via `@skip` or `@include` directives. 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. +`{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. +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. @@ -867,14 +868,15 @@ of rules must be adhered to by every Object type in a GraphQL schema. 1. An Object type must define one or more fields. 2. For each field of an Object type: - 1. The field must have a unique name within that Object type; - no two fields may share the same name. - 2. The field must not have a name which begins with the - characters {"__"} (two underscores). - 3. The field must return a type where {IsOutputType(fieldType)} returns {true}. + 1. The field must have a unique name within that Object type; no two fields + may share the same name. + 2. The field must not have a name which begins with the characters {"\_\_"} + (two underscores). + 3. The field must return a type where {IsOutputType(fieldType)} returns + {true}. 4. For each argument of the field: - 1. The argument must not have a name which begins with the - characters {"__"} (two underscores). + 1. The argument must not have a name which begins with the characters + {"\_\_"} (two underscores). 2. The argument must accept a type where {IsInputType(argumentType)} returns {true}. 3. An object type may declare that it implements one or more unique interfaces. @@ -885,47 +887,48 @@ of rules must be adhered to by every Object type in a GraphQL schema. IsValidImplementation(type, implementedType): - 1. If {implementedType} declares it implements any interfaces, - {type} must also declare it implements those interfaces. - 2. {type} must include a field of the same name for every field - defined in {implementedType}. - 1. Let {field} be that named field on {type}. - 2. Let {implementedField} be that named field on {implementedType}. - 3. {field} must include an argument of the same name for every argument - defined in {implementedField}. - 1. That named argument on {field} must accept the same type - (invariant) as that named argument on {implementedField}. - 4. {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. - 5. {field} must return a type which is equal to or a sub-type of - (covariant) the return type of {implementedField} field's return type: - 1. Let {fieldType} be the return type of {field}. - 2. Let {implementedFieldType} be the return type of {implementedField}. - 3. {IsValidImplementationFieldType(fieldType, implementedFieldType)} - must be {true}. +1. If {implementedType} declares it implements any interfaces, {type} must also + declare it implements those interfaces. +2. {type} must include a field of the same name for every field defined in + {implementedType}. + 1. Let {field} be that named field on {type}. + 2. Let {implementedField} be that named field on {implementedType}. + 3. {field} must include an argument of the same name for every argument + defined in {implementedField}. + 1. That named argument on {field} must accept the same type (invariant) + as that named argument on {implementedField}. + 4. {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. + 5. {field} must return a type which is equal to or a sub-type of (covariant) + the return type of {implementedField} field's return type: + 1. Let {fieldType} be the return type of {field}. + 2. Let {implementedFieldType} be the return type of {implementedField}. + 3. {IsValidImplementationFieldType(fieldType, implementedFieldType)} must + be {true}. IsValidImplementationFieldType(fieldType, implementedFieldType): - 1. If {fieldType} is a Non-Null type: - 1. Let {nullableType} be the unwrapped nullable type of {fieldType}. - 2. Let {implementedNullableType} be the unwrapped nullable type - of {implementedFieldType} if it is a Non-Null type, otherwise let it be - {implementedFieldType} directly. - 3. Return {IsValidImplementationFieldType(nullableType, implementedNullableType)}. - 2. If {fieldType} is a List type and {implementedFieldType} is also a List type: - 1. Let {itemType} be the unwrapped item type of {fieldType}. - 2. Let {implementedItemType} be the unwrapped item type - of {implementedFieldType}. - 3. Return {IsValidImplementationFieldType(itemType, implementedItemType)}. - 3. If {fieldType} is the same type as {implementedFieldType} then return {true}. - 4. If {fieldType} is an Object type and {implementedFieldType} is - a Union type and {fieldType} is a possible type of {implementedFieldType} - then return {true}. - 5. If {fieldType} is an Object or Interface type and {implementedFieldType} - is an Interface type and {fieldType} declares it implements - {implementedFieldType} then return {true}. - 6. Otherwise return {false}. +1. If {fieldType} is a Non-Null type: + 1. Let {nullableType} be the unwrapped nullable type of {fieldType}. + 2. Let {implementedNullableType} be the unwrapped nullable type of + {implementedFieldType} if it is a Non-Null type, otherwise let it be + {implementedFieldType} directly. + 3. Return {IsValidImplementationFieldType(nullableType, + implementedNullableType)}. +2. If {fieldType} is a List type and {implementedFieldType} is also a List type: + 1. Let {itemType} be the unwrapped item type of {fieldType}. + 2. Let {implementedItemType} be the unwrapped item type of + {implementedFieldType}. + 3. Return {IsValidImplementationFieldType(itemType, implementedItemType)}. +3. If {fieldType} is the same type as {implementedFieldType} then return {true}. +4. If {fieldType} is an Object type and {implementedFieldType} is a Union type + and {fieldType} is a possible type of {implementedFieldType} then return + {true}. +5. If {fieldType} is an Object or Interface type and {implementedFieldType} is + an Interface type and {fieldType} declares it implements + {implementedFieldType} then return {true}. +6. Otherwise return {false}. ### Field Arguments @@ -939,7 +942,7 @@ 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 +{"\_\_"} (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 @@ -952,8 +955,8 @@ type Person { } ``` -Operations can optionally specify arguments to their fields to provide -these arguments. +Operations can optionally specify arguments to their fields to provide these +arguments. This example operation: @@ -976,13 +979,12 @@ May return the result: The type of an object field argument must be an input type (any type except an Object, Interface, or Union type). - ### Field Deprecation Fields in an object may be marked as deprecated as deemed necessary by the -application. It is still legal to include these fields in a selection set -(to ensure existing clients are not broken by the change), but the fields should -be appropriately treated in documentation and tooling. +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: @@ -993,13 +995,13 @@ type ExampleType { } ``` - ### Object Extensions ObjectTypeExtension : - - extend type Name ImplementsInterfaces? Directives[Const]? FieldsDefinition - - extend type Name ImplementsInterfaces? Directives[Const] [lookahead != `{`] - - extend type Name ImplementsInterfaces [lookahead != `{`] + +- extend type Name ImplementsInterfaces? Directives[Const]? FieldsDefinition +- extend type Name ImplementsInterfaces? Directives[Const] [lookahead != `{`] +- extend type Name ImplementsInterfaces [lookahead != `{`] 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 @@ -1031,19 +1033,21 @@ Object type extensions have the potential to be invalid if incorrectly defined. may share the same name. 3. Any fields of an Object type extension must not be already defined on the original Object type. -4. Any non-repeatable directives provided must not already apply to the - original Object type. +4. Any non-repeatable directives provided must not already apply to the original + Object type. 5. Any interfaces provided must not be already implemented by the original Object type. 6. The resulting extended object type must be a super-set of all interfaces it implements. - ## Interfaces InterfaceTypeDefinition : - - Description? interface Name ImplementsInterfaces? Directives[Const]? FieldsDefinition - - Description? interface Name ImplementsInterfaces? Directives[Const]? [lookahead != `{`] + +- Description? interface Name ImplementsInterfaces? Directives[Const]? + FieldsDefinition +- Description? interface Name ImplementsInterfaces? Directives[Const]? + [lookahead != `{`] GraphQL interfaces represent a list of named fields and their arguments. GraphQL objects and interfaces can then implement these interfaces which requires that @@ -1106,8 +1110,8 @@ common fields. } ``` -When selecting fields on an interface type, only those fields declared on -the interface may be queried. In the above example, `entity` returns a +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`: @@ -1193,7 +1197,6 @@ interface Named implements Node & Named { } ``` - **Result Coercion** The interface type should have some way of determining which object a given @@ -1210,15 +1213,15 @@ Interface types have the potential to be invalid if incorrectly defined. 1. An Interface type must define one or more fields. 2. For each field of an Interface type: - 1. The field must have a unique name within that Interface type; - no two fields may share the same name. - 2. The field must not have a name which begins with the - characters {"__"} (two underscores). - 3. The field must return a type where {IsOutputType(fieldType)} - returns {true}. + 1. The field must have a unique name within that Interface type; no two + fields may share the same name. + 2. The field must not have a name which begins with the characters {"\_\_"} + (two underscores). + 3. The field must return a type where {IsOutputType(fieldType)} returns + {true}. 4. For each argument of the field: - 1. The argument must not have a name which begins with the - characters {"__"} (two underscores). + 1. The argument must not have a name which begins with the characters + {"\_\_"} (two underscores). 2. The argument must accept a type where {IsInputType(argumentType)} returns {true}. 3. An interface type may declare that it implements one or more unique @@ -1228,13 +1231,15 @@ Interface types have the potential to be invalid if incorrectly defined. 2. For each interface declared implemented as {implementedType}, {IsValidImplementation(implementingType, implementedType)} must be {true}. - ### Interface Extensions InterfaceTypeExtension : - - extend interface Name ImplementsInterfaces? Directives[Const]? FieldsDefinition - - extend interface Name ImplementsInterfaces? Directives[Const] [lookahead != `{`] - - extend interface Name ImplementsInterfaces [lookahead != `{`] + +- extend interface Name ImplementsInterfaces? Directives[Const]? + FieldsDefinition +- extend interface Name ImplementsInterfaces? Directives[Const] [lookahead != + `{`] +- extend interface Name ImplementsInterfaces [lookahead != `{`] Interface type extensions are used to represent an interface which has been extended from some original interface. For example, this might be used to @@ -1261,8 +1266,8 @@ extend type Business { 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: +In this example, a directive is added to a `NamedEntity` type without adding +fields: ```graphql example extend interface NamedEntity @addedDirective @@ -1270,7 +1275,8 @@ extend interface NamedEntity @addedDirective **Type Validation** -Interface type extensions have the potential to be invalid if incorrectly defined. +Interface type extensions have the potential to be invalid if incorrectly +defined. 1. The named type must already be defined and must be an Interface type. 2. The fields of an Interface type extension must have unique names; no two @@ -1280,31 +1286,32 @@ Interface type extensions have the potential to be invalid if incorrectly define 4. 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). -5. Any non-repeatable directives provided must not already apply to the - original Interface type. +5. Any non-repeatable directives provided must not already apply to the original + Interface type. 6. The resulting extended Interface type must be a super-set of all Interfaces it implements. - ## Unions -UnionTypeDefinition : Description? union Name Directives[Const]? UnionMemberTypes? +UnionTypeDefinition : Description? union Name Directives[Const]? +UnionMemberTypes? UnionMemberTypes : - - UnionMemberTypes | NamedType - - = `|`? NamedType -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. +- UnionMemberTypes | NamedType +- = `|`? NamedType + +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}). +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: @@ -1378,21 +1385,21 @@ Unions are never valid inputs. Union types have the potential to be invalid if incorrectly defined. 1. A Union type must include one or more unique member types. -2. The member types of a Union type must all be Object base types; - Scalar, Interface and Union types must not be member types of a Union. - Similarly, wrapping types must not be member types of a Union. - +2. The member types of a Union type must all be Object base types; Scalar, + Interface and Union types must not be member types of a Union. Similarly, + wrapping types must not be member types of a Union. ### Union Extensions UnionTypeExtension : - - extend union Name Directives[Const]? UnionMemberTypes - - extend union Name Directives[Const] -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. +- extend union Name Directives[Const]? UnionMemberTypes +- extend union Name Directives[Const] + +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** @@ -1405,14 +1412,15 @@ Union type extensions have the potential to be invalid if incorrectly defined. 3. All member types of a Union type extension must be unique. 4. All member types of a Union type extension must not already be a member of the original Union type. -5. Any non-repeatable directives provided must not already apply to the - original Union type. +5. Any non-repeatable directives provided must not already apply to the original + Union type. ## Enums EnumTypeDefinition : - - Description? enum Name Directives[Const]? EnumValuesDefinition - - Description? enum Name Directives[Const]? [lookahead != `{`] + +- Description? enum Name Directives[Const]? EnumValuesDefinition +- Description? enum Name Directives[Const]? [lookahead != `{`] EnumValuesDefinition : { EnumValueDefinition+ } @@ -1443,13 +1451,14 @@ 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. +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](https://github.com/edn-format/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. +Variable transport serializations which have a different representation for +non-string symbolic values (for example, +[EDN](https://github.com/edn-format/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** @@ -1457,17 +1466,17 @@ Enum types have the potential to be invalid if incorrectly defined. 1. An Enum type must define one or more unique enum values. - ### Enum Extensions EnumTypeExtension : - - extend enum Name Directives[Const]? EnumValuesDefinition - - extend enum Name Directives[Const] [lookahead != `{`] -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. +- extend enum Name Directives[Const]? EnumValuesDefinition +- extend enum Name Directives[Const] [lookahead != `{`] + +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** @@ -1475,25 +1484,25 @@ Enum type extensions have the potential to be invalid if incorrectly defined. 1. The named type must already be defined and must be an Enum type. 2. All values of an Enum type extension must be unique. -3. All values of an Enum type extension must not already be a value of - the original Enum. -4. Any non-repeatable directives provided must not already apply to the - original Enum type. - +3. All values of an Enum type extension must not already be a value of the + original Enum. +4. Any non-repeatable directives provided must not already apply to the original + Enum type. ## Input Objects InputObjectTypeDefinition : - - Description? input Name Directives[Const]? InputFieldsDefinition - - Description? input Name Directives[Const]? [lookahead != `{`] + +- Description? input Name Directives[Const]? InputFieldsDefinition +- Description? input Name Directives[Const]? [lookahead != `{`] InputFieldsDefinition : { InputValueDefinition+ } 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 +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: @@ -1582,27 +1591,27 @@ 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 +- 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 +- 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 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. +- 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`: @@ -1614,86 +1623,86 @@ input ExampleInputObject { } ``` -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} +| 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** 1. An Input Object type must define one or more input fields. 2. For each input field of an Input Object type: - 1. The input field must have a unique name within that Input Object type; - no two input fields may share the same name. - 2. The input field must not have a name which begins with the - characters {"__"} (two underscores). + 1. The input field must have a unique name within that Input Object type; no + two input fields may share the same name. + 2. The input field must not have a name which begins with the characters + {"\_\_"} (two underscores). 3. The input field must accept a type where {IsInputType(inputFieldType)} returns {true}. 3. If an Input Object references itself either directly or through referenced Input Objects, at least one of the fields in the chain of references must be either a nullable or a List type. - ### Input Object Extensions InputObjectTypeExtension : - - extend input Name Directives[Const]? InputFieldsDefinition - - extend input Name Directives[Const] [lookahead != `{`] + +- extend input Name Directives[Const]? InputFieldsDefinition +- extend input Name Directives[Const] [lookahead != `{`] 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. +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. +Input object type extensions have the potential to be invalid if incorrectly +defined. 1. The named type must already be defined and must be a Input Object type. 2. All fields of an Input Object type extension must have unique names. 3. All fields of an Input Object type extension must not already be a field of the original Input Object. -4. Any non-repeatable directives provided must not already apply to the - original Input Object type. - +4. Any non-repeatable directives provided must not already apply to the original + Input Object type. ## List -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 +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]]`. +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. +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. +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. Note: See [Handling Field Errors](#sec-Handling-Field-Errors) for more about this behavior. @@ -1703,10 +1712,10 @@ this behavior. 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). +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 @@ -1715,57 +1724,55 @@ 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` - +| 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` | ## Non-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!`. +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 +Fields are _always_ optional within the context of a selection set, a field may be omitted and the selection set is still valid. However fields that return Non-Null types will never return the value {null} if queried. 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. +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. +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. Note: When a field error is raised on a non-null value, the error propagates to -the parent field. For more information on this process, see -"Errors and Non-Nullability" within the Execution section. +the parent field. For more information on this process, see "Errors and +Non-Nullability" within the Execution section. **Input Coercion** If an argument or input-object field of a Non-Null type is not provided, is provided with the literal value {null}, or is provided with a variable that was -either not provided a value at runtime, or was provided the value {null}, then -a request error must be raised. +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 @@ -1795,14 +1802,13 @@ query withNullableVariable($var: String) { } ``` -Note: The Validation section defines providing a nullable variable type to -a non-null input type as invalid. +Note: The Validation section defines providing a nullable variable type to a +non-null input type as invalid. **Type Validation** 1. A Non-Null type must not wrap another Non-Null type. - ### Combining List and Non-Null The List and Non-Null wrapping types can compose, representing more complex @@ -1816,60 +1822,64 @@ 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 - +| 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 | ## Directives -DirectiveDefinition : Description? directive @ Name ArgumentsDefinition? `repeatable`? on DirectiveLocations +DirectiveDefinition : Description? directive @ Name ArgumentsDefinition? +`repeatable`? on DirectiveLocations DirectiveLocations : - - DirectiveLocations | DirectiveLocation - - `|`? DirectiveLocation + +- DirectiveLocations | DirectiveLocation +- `|`? DirectiveLocation DirectiveLocation : - - ExecutableDirectiveLocation - - TypeSystemDirectiveLocation + +- ExecutableDirectiveLocation +- TypeSystemDirectiveLocation ExecutableDirectiveLocation : one of - - `QUERY` - - `MUTATION` - - `SUBSCRIPTION` - - `FIELD` - - `FRAGMENT_DEFINITION` - - `FRAGMENT_SPREAD` - - `INLINE_FRAGMENT` - - `VARIABLE_DEFINITION` + +- `QUERY` +- `MUTATION` +- `SUBSCRIPTION` +- `FIELD` +- `FRAGMENT_DEFINITION` +- `FRAGMENT_SPREAD` +- `INLINE_FRAGMENT` +- `VARIABLE_DEFINITION` TypeSystemDirectiveLocation : one of - - `SCHEMA` - - `SCALAR` - - `OBJECT` - - `FIELD_DEFINITION` - - `ARGUMENT_DEFINITION` - - `INTERFACE` - - `UNION` - - `ENUM` - - `ENUM_VALUE` - - `INPUT_OBJECT` - - `INPUT_FIELD_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 @@ -1877,41 +1887,41 @@ 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. +:: A _built-in directive_ is any directive defined within this specification. GraphQL implementations should provide the `@skip` and `@include` directives. GraphQL implementations that support the type system definition language must -provide the `@deprecated` directive if representing deprecated portions of -the schema. +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. +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 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. +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. +:: 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. -Note: When defining a *custom directive*, it is recommended to prefix the +Note: When defining a _custom directive_, it is recommended to prefix the directive's name to make its scope of usage clear and to prevent a collision -with *built-in directive* which may be specified by future versions of this -document (which will not include `_` 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](https://github.com/graphql/graphql-spec/blob/main/CONTRIBUTING.md). +with _built-in directive_ which may be specified by future versions of this +document (which will not include `_` 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](https://github.com/graphql/graphql-spec/blob/main/CONTRIBUTING.md). 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: +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: ```graphql example directive @example on FIELD @@ -1931,12 +1941,13 @@ directive @example on | 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. +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: +In this example, the directive `@example` annotates field and argument +definitions: ```graphql example directive @example on FIELD_DEFINITION | ARGUMENT_DEFINITION @@ -1949,8 +1960,8 @@ type SomeType { 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: +additional information needs to be provided to a type or schema extension via a +directive: ```graphql example directive @delegateField(name: String!) repeatable on OBJECT | INTERFACE @@ -1978,13 +1989,13 @@ repeatable directives. 2. 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. -3. The directive must not have a name which begins with the characters - {"__"} (two underscores). +3. The directive must not have a name which begins with the characters {"\_\_"} + (two underscores). 4. For each argument of the directive: - 1. The argument must not have a name which begins with the - characters {"__"} (two underscores). - 2. The argument must accept a type where {IsInputType(argumentType)} - returns {true}. + 1. The argument must not have a name which begins with the characters + {"\_\_"} (two underscores). + 2. The argument must accept a type where {IsInputType(argumentType)} returns + {true}. ### @skip @@ -1992,7 +2003,7 @@ repeatable directives. directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT ``` -The `@skip` *built-in directive* may be provided for fields, fragment spreads, +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. @@ -2005,14 +2016,13 @@ query myQuery($someTest: Boolean!) { } ``` - ### @include ```graphql directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT ``` -The `@include` *built-in directive* may be provided for fields, 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. @@ -2026,12 +2036,11 @@ query myQuery($someTest: Boolean!) { ``` Note: Neither `@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. - +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. ### @deprecated @@ -2041,15 +2050,15 @@ directive @deprecated( ) on FIELD_DEFINITION | ENUM_VALUE ``` -The `@deprecated` *built-in directive* is used within the type system definition +The `@deprecated` _built-in directive_ is used within the type system definition language to indicate deprecated portions of a GraphQL service's schema, such as deprecated fields on a type or deprecated enum values. Deprecations include a reason for why it is deprecated, which is formatted using Markdown syntax (as specified by [CommonMark](https://commonmark.org/)). -In this example type definition, `oldField` is deprecated in favor of -using `newField`. +In this example type definition, `oldField` is deprecated in favor of using +`newField`. ```graphql example type ExampleType { @@ -2058,15 +2067,14 @@ type ExampleType { } ``` - ### @specifiedBy ```graphql 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 +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](#sec-Scalars.Custom-Scalars). The URL should point to a human-readable specification of the data format, serialization, and coercion rules. It must not appear on built-in scalar types. diff --git a/spec/Section 4 -- Introspection.md b/spec/Section 4 -- Introspection.md index 9adb20b94..9b32133f8 100644 --- a/spec/Section 4 -- Introspection.md +++ b/spec/Section 4 -- Introspection.md @@ -3,8 +3,8 @@ 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. +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: @@ -59,12 +59,12 @@ would produce the result: **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 +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 {"__"}. +Otherwise, any {Name} within a GraphQL type system must not start with two +underscores {"\_\_"}. ## Type Name Introspection @@ -101,10 +101,10 @@ the root type of the query operation. 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](https://commonmark.org/)). Therefore it is -recommended that any tool that displays `description` use a CommonMark-compliant -Markdown renderer. +capabilities. A GraphQL service may return the `description` field using +Markdown syntax (as specified by [CommonMark](https://commonmark.org/)). +Therefore it is recommended that any tool that displays `description` use a +CommonMark-compliant Markdown renderer. **Deprecation** @@ -217,34 +217,33 @@ enum __DirectiveLocation { } ``` -### The __Schema Type +### The \_\_Schema Type -The `__Schema` type is returned from the `__schema` meta-field and provides -all information about the schema of a GraphQL service. +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. +- `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. +- `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. +- `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. +- `directives` must return the set of all directives available within this + schema including all built-in directives. - -### The __Type Type +### The \_\_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). +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, +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 @@ -253,31 +252,31 @@ 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"} +- {"OBJECT"} +- {"INTERFACE"} +- {"UNION"} +- {"ENUM"} +- {"INPUT_OBJECT"} +- {"LIST"} +- {"NON_NULL"} **Scalar** -Represents scalar types such as Int, String, and Boolean. Scalars cannot have fields. +Represents scalar types such as Int, String, and Boolean. Scalars cannot have +fields. Also represents [Custom scalars](#sec-Scalars.Custom-Scalars) which may provide -`specifiedByURL` as a *scalar specification URL*. +`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 +- `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}. - +- All other fields must return {null}. **Object** @@ -286,32 +285,30 @@ 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 +- `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. -* `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}. - +- `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. +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 +- `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}. - +- All other fields must return {null}. **Interface** @@ -322,18 +319,17 @@ 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 +- `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. -* `interfaces` must return the set of interfaces that an object 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}. - +- `interfaces` must return the set of interfaces that an object 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** @@ -341,21 +337,20 @@ 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`. +- `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 + - Accepts the argument `includeDeprecated` which defaults to {false}. If {true}, deprecated enum values are also returned. -* All other fields must return {null}. - +- 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. +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: @@ -368,12 +363,11 @@ input Point { 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`. -* All other fields must return {null}. - +- `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`. +- All other fields must return {null}. **List** @@ -386,10 +380,9 @@ 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}. - +- `kind` must return `__TypeKind.LIST`. +- `ofType` must return a type of any kind. +- All other fields must return {null}. **Non-Null** @@ -405,91 +398,90 @@ 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}. +- `kind` must return `__TypeKind.NON_NULL`. +- `ofType` must return a type of any kind except Non-Null. +- All other fields must return {null}. - -### The __Field Type +### The \_\_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. -* `type` must return a `__Type` that represents the type of value returned by +- `name` must return a String +- `description` may return a String or {null} +- `args` returns a List of `__InputValue` representing the arguments this field + accepts. +- `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, +- `isDeprecated` returns {true} if this field should no longer be used, otherwise {false}. -* `deprecationReason` optionally provides a reason why this field is deprecated. - +- `deprecationReason` optionally provides a reason why this field is deprecated. -### The __InputValue Type +### The \_\_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 +- `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}. -### The __EnumValue Type +### The \_\_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, +- `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. +- `deprecationReason` optionally provides a reason why this enum value is + deprecated. -### The __Directive Type +### The \_\_Directive Type The `__Directive` type represents a directive that a service supports. -This includes both any *built-in directive* and any *custom directive*. +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"} +- {"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 +- `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 +- `args` returns a List of `__InputValue` representing the arguments this directive accepts. -* `isRepeatable` must return a Boolean that indicates if the directive may be +- `isRepeatable` must return a Boolean that indicates if the directive may be used repeatedly at a single location. diff --git a/spec/Section 5 -- Validation.md b/spec/Section 5 -- Validation.md index 1b90d6f58..4eda8e7b4 100644 --- a/spec/Section 5 -- Validation.md +++ b/spec/Section 5 -- Validation.md @@ -9,31 +9,31 @@ 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. +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 +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. +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: +For this section of this schema, we will assume the following type system in +order to demonstrate examples: ```graphql example type Query { @@ -89,15 +89,14 @@ union DogOrHuman = Dog | Human union HumanOrAlien = Human | Alien ``` - ## Documents ### Executable Definitions **Formal Specification** -* For each definition {definition} in the document. -* {definition} must be {ExecutableDefinition} (it must not be +- For each definition {definition} in the document. +- {definition} must be {ExecutableDefinition} (it must not be {TypeSystemDefinitionOrExtension}). **Explanatory Text** @@ -136,11 +135,12 @@ extend type Dog { **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. +- 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** @@ -205,10 +205,10 @@ mutation dogOperation { **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. +- 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** @@ -249,13 +249,13 @@ query getName { **Formal Specification** -* For each subscription operation definition {subscription} in the document -* Let {subscriptionType} be the root Subscription type in {schema}. -* 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 +- For each subscription operation definition {subscription} in the document +- Let {subscriptionType} be the root Subscription type in {schema}. +- 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** @@ -334,9 +334,9 @@ 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 +- For each {selection} in the document. +- Let {fieldName} be the target field of {selection} +- {fieldName} must be defined on type in scope **Explanatory Text** @@ -355,8 +355,8 @@ fragment aliasedLyingFieldTargetNotDefined on Dog { } ``` -For interfaces, direct field selection can only be done on fields. Fields -of concrete implementors are not relevant to the validity of the given +For interfaces, direct field selection can only be done on fields. Fields of +concrete implementors are not relevant to the validity of the given interface-typed selection set. For example, the following is valid: @@ -376,9 +376,9 @@ fragment definedOnImplementorsButNotInterface on Pet { ``` 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. +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: @@ -403,59 +403,58 @@ fragment directFieldSelectionOnUnion on CatOrDog { } ``` - ### Field Selection Merging **Formal Specification** -* Let {set} be any selection set defined in the GraphQL document. -* {FieldsInSetCanMerge(set)} must be true. +- Let {set} be any selection set defined in the GraphQL document. +- {FieldsInSetCanMerge(set)} must be true. FieldsInSetCanMerge(set): - * 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. - * 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 {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. + - 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. SameResponseShape(fieldA, fieldB): - * 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} and {typeB} are both composite types. - * 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. +- 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} and {typeB} are both composite types. +- 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. **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. +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 {MergeSelectionSets()} and {CollectFields()}. @@ -486,8 +485,8 @@ fragment conflictingBecauseAlias on Dog { } ``` -Identical fields are also merged if they have identical arguments. Both -values and variables can be correctly merged. +Identical fields are also merged if they have identical arguments. Both values +and variables can be correctly merged. For example the following correctly merge: @@ -565,22 +564,21 @@ fragment conflictingDifferingResponses on Pet { } ``` - ### Leaf Field Selections **Formal Specification** -* For each {selection} in the document -* Let {selectionType} be the 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 +- For each {selection} in the document +- Let {selectionType} be the 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** -Field selections on scalars or enums are never allowed, because they -are the leaf nodes of any GraphQL operation. +Field selections on scalars or enums are never allowed, because they are the +leaf nodes of any GraphQL operation. The following is valid. @@ -600,12 +598,12 @@ fragment scalarSelectionsNotAllowedOnInt on Dog { } ``` -Conversely the leaf field selections of GraphQL operations -must be of type scalar or enum. Leaf selections on objects, interfaces, -and unions without subfields are disallowed. +Conversely the leaf field selections of GraphQL operations must be of type +scalar or enum. Leaf selections on objects, interfaces, and unions without +subfields are disallowed. -Let's assume the following additions to the query root operation type of -the schema: +Let's assume the following additions to the query root operation type of the +schema: ```graphql example extend type Query { @@ -631,21 +629,20 @@ query directQueryOnUnionWithoutSubFields { } ``` - ## Arguments Arguments are provided to both fields and directives. The following validation rules apply in both cases. - ### Argument 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. +- 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** @@ -699,7 +696,8 @@ extend type Query { } ``` -Order does not matter in arguments. Therefore both the following examples are valid. +Order does not matter in arguments. Therefore both the following examples are +valid. ```graphql example fragment multipleArgs on Arguments { @@ -711,35 +709,35 @@ fragment multipleArgsReverseOrder on Arguments { } ``` - ### Argument 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. +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}. - +- 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}. #### Required 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. +- 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** @@ -793,18 +791,19 @@ fragment missingRequiredArg on Arguments { **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. +- 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. +Inline fragments are not considered fragment definitions, and are unaffected by +this validation rule. For example the following document is valid: @@ -847,20 +846,19 @@ fragment fragmentOne on Dog { } ``` - #### Fragment 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 +- 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. +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: @@ -896,14 +894,12 @@ fragment inlineNotExistingType on Dog { } ``` - #### Fragments On Composite Types **Formal Specification** -* For each {fragment} defined in the document. -* The target type of fragment must have kind {UNION}, {INTERFACE}, or - {OBJECT}. +- For each {fragment} defined in the document. +- The target type of fragment must have kind {UNION}, {INTERFACE}, or {OBJECT}. **Explanatory Text** @@ -943,13 +939,12 @@ fragment inlineFragOnScalar on Dog { } ``` - #### Fragments 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 +- For each {fragment} defined in the document. +- {fragment} must be the target of at least one spread in the document **Explanatory Text** @@ -969,28 +964,24 @@ fragment nameFragment on Dog { # unused } ``` - ### Fragment 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. - +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. #### Fragment 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 +- 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. +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. ```graphql counter-example { @@ -1000,29 +991,28 @@ not defined. } ``` - #### Fragment spreads must not form cycles **Formal Specification** -* For each {fragmentDefinition} in the document -* Let {visited} be the empty set. -* {DetectFragmentCycles(fragmentDefinition, visited)} +- For each {fragmentDefinition} in the document +- Let {visited} be the empty set. +- {DetectFragmentCycles(fragmentDefinition, visited)} 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)} +- 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. +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: @@ -1086,39 +1076,35 @@ fragment ownerFragment on Human { } ``` - #### Fragment 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. +- 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. GetPossibleTypes(type): - * 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} +- 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. - +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. ##### Object 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. +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 @@ -1140,11 +1126,10 @@ fragment catInDogFragmentInvalid on Dog { } ``` - ##### Abstract 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. +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 @@ -1174,18 +1159,16 @@ fragment unionWithObjectFragment on Dog { } ``` -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. - +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. ##### Object 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. +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: @@ -1205,8 +1188,7 @@ fragment catOrDogFragment on CatOrDog { ``` {petFragment} is valid because {Dog} implements the interface {Pet}. -{catOrDogFragment} is valid because {Cat} is a member of the -{CatOrDog} union. +{catOrDogFragment} is valid because {Cat} is a member of the {CatOrDog} union. By contrast the following fragments are invalid: @@ -1229,11 +1211,10 @@ fragment humanOrAlienFragment on HumanOrAlien { is invalid. Likewise {Cat} is not a member of the union {HumanOrAlien}, and it can also never return meaningful results, making it invalid. - ##### Abstract 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 +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 @@ -1250,8 +1231,8 @@ fragment dogOrHumanFragment on DogOrHuman { } ``` -is considered valid because {Dog} implements interface {Pet} and is a -member of {DogOrHuman}. +is considered valid because {Dog} implements interface {Pet} and is a member of +{DogOrHuman}. However @@ -1265,14 +1246,13 @@ fragment sentientFragment on Sentient { } ``` -is not valid because there exists no type that implements both {Pet} -and {Sentient}. - +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. +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`. @@ -1296,26 +1276,25 @@ fragment resourceFragment on Resource { } ``` - ## Values - ### Values 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}. +- 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 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: @@ -1334,8 +1313,8 @@ query goodComplexDefaultValue($search: ComplexInput = { name: "Fido" }) { } ``` -Non-coercible values (such as a String into an Int) are invalid. The -following examples are invalid: +Non-coercible values (such as a String into an Int) are invalid. The following +examples are invalid: ```graphql counter-example fragment stringIntoInt on Arguments { @@ -1347,16 +1326,15 @@ query badComplexValue { } ``` - ### Input 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 +- 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. +- {inputFieldDefinition} must exist. **Explanatory Text** @@ -1380,16 +1358,15 @@ which is not defined on the expected type: } ``` - ### Input 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 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}. **Explanatory Text** @@ -1404,23 +1381,23 @@ For example the following document will not pass validation. } ``` - ### Input 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. +- 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** @@ -1429,41 +1406,38 @@ 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. - ## Directives - ### Directives 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. +- 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. - +GraphQL services define what directives they support. For each usage of a +directive, the directive must be available on that service. ### Directives 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}. +- 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. +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. @@ -1474,19 +1448,18 @@ query @skip(if: $foo) { } ``` - ### Directives 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} +- 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. + - {namedDirectives} must be a set of one. **Explanatory Text** @@ -1505,8 +1478,8 @@ query ($foo: Boolean = true, $bar: Boolean = false) { ``` 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: +per location, despite being used twice in the operation and on the same named +field: ```raw graphql example query ($foo: Boolean = true, $bar: Boolean = false) { @@ -1519,19 +1492,18 @@ query ($foo: Boolean = true, $bar: Boolean = false) { } ``` - ## Variables ### Variable 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 +- 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 + - {variables} must be a set of one **Explanatory Text** @@ -1547,7 +1519,6 @@ query houseTrainedQuery($atOtherHomes: Boolean, $atOtherHomes: Boolean) { } ``` - 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: @@ -1567,15 +1538,14 @@ fragment HouseTrainedFragment on Query { } ``` - ### Variables 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 {operation} in a {document} +- For every {variable} on each {operation} + - Let {variableType} be the type of {variable} + - {IsInputType(variableType)} must be {true} **Explanatory Text** @@ -1636,22 +1606,23 @@ query takesCatOrDog($catOrDog: CatOrDog) { } ``` - ### All 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 +- 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 +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: @@ -1679,9 +1650,9 @@ query variableIsNotDefined { ${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. +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: @@ -1701,8 +1672,8 @@ 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. +On the other hand, if a fragment is included within an operation that does not +define a referenced variable, the document is invalid. ```graphql counter-example query variableIsNotDefinedUsedInSingleFragment { @@ -1734,8 +1705,7 @@ fragment isHouseTrainedFragment on Dog { } ``` -Variables must be defined in all operations in which a fragment -is used. +Variables must be defined in all operations in which a fragment is used. ```graphql example query houseTrainedQueryOne($atOtherHomes: Boolean) { @@ -1775,26 +1745,25 @@ fragment isHouseTrainedFragment on Dog { } ``` -This is because {houseTrainedQueryTwoNotDefined} does not define -a variable ${atOtherHomes} but that variable is used by {isHouseTrainedFragment} -which is included in that operation. - +This is because {houseTrainedQueryTwoNotDefined} does not define a variable +${atOtherHomes} but that variable is used by {isHouseTrainedFragment} which is +included in that operation. ### All 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 +- 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. +fragment transitively included by that operation. Unused variables cause a +validation error. For example the following is invalid: @@ -1825,7 +1794,8 @@ fragment isHouseTrainedFragment on Dog { 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: +If that fragment did not have a reference to ${atOtherHomes} it would be not +valid: ```graphql counter-example query variableNotUsedWithinFragment($atOtherHomes: Boolean) { @@ -1861,63 +1831,64 @@ fragment isHouseTrainedFragment on Dog { } ``` -This document is not valid because {queryWithExtraVar} defines -an extraneous variable. - +This document is not valid because {queryWithExtraVar} defines an extraneous +variable. ### All 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} +- 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}. + - {IsVariableUsageAllowed(variableDefinition, variableUsage)} must be {true}. IsVariableUsageAllowed(variableDefinition, variableUsage): - * 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)}. +- 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)}. 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}. +- 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. +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: @@ -1929,10 +1900,11 @@ query intCannotGoIntoBoolean($intArg: Int) { } ``` -${intArg} typed as {Int} cannot be used as an argument to {booleanArg}, typed as {Boolean}. +${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. +List cardinality must also be the same. For example, lists cannot be passed into +singular values. ```graphql counter-example query booleanListCannotGoIntoBoolean($booleanListArg: [Boolean]) { @@ -1942,8 +1914,8 @@ query booleanListCannotGoIntoBoolean($booleanListArg: [Boolean]) { } ``` -Nullability must also be respected. In general a nullable variable cannot -be passed to a non-null argument. +Nullability must also be respected. In general a nullable variable cannot be +passed to a non-null argument. ```graphql counter-example query booleanArgQuery($booleanArg: Boolean) { @@ -1953,10 +1925,10 @@ query booleanArgQuery($booleanArg: Boolean) { } ``` -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: +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: ```graphql example query nonNullListToList($nonNullBooleanList: [Boolean]!) { @@ -1998,8 +1970,8 @@ query booleanArgQueryWithDefault($booleanArg: Boolean) { ``` 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 +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. @@ -2012,5 +1984,5 @@ query booleanArgQueryWithDefault($booleanArg: Boolean = true) { } ``` -Note: The value {null} could still be provided to such a variable at runtime. -A non-null argument must raise a field error if provided a {null} value. +Note: The value {null} could still be provided to such a variable at runtime. A +non-null argument must raise a field error if provided a {null} value. diff --git a/spec/Section 6 -- Execution.md b/spec/Section 6 -- Execution.md index d1fba6a4a..a0f3400d4 100644 --- a/spec/Section 6 -- Execution.md +++ b/spec/Section 6 -- Execution.md @@ -4,19 +4,19 @@ GraphQL generates a response from a request via execution. A request for execution consists of a few pieces of information: -* The schema to use, typically solely provided by the GraphQL service. -* A {Document} which must contain GraphQL {OperationDefinition} and may contain {FragmentDefinition}. -* Optionally: The name of the Operation in the Document to execute. -* Optionally: Values for any Variables defined by the Operation. -* 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. +- The schema to use, typically solely provided by the GraphQL service. +- A {Document} which must contain GraphQL {OperationDefinition} and may contain + {FragmentDefinition}. +- Optionally: The name of the Operation in the Document to execute. +- Optionally: Values for any Variables defined by the Operation. +- 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()} produces the response, to be formatted according to the Response section below. - ## Executing Requests To execute a request, the executor must have a parsed {Document} and a selected @@ -27,26 +27,28 @@ request is determined by the result of executing this operation according to the ExecuteRequest(schema, document, operationName, variableValues, initialValue): - * 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)}. +- 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)}. GetOperation(document, operationName): - * If {operationName} is {null}: - * If {document} contains exactly one operation. - * Return the Operation contained in the {document}. - * Otherwise raise a request error requiring {operationName}. - * Otherwise: - * Let {operation} be the Operation named {operationName} in {document}. - * If {operation} was not found, raise a request error. - * Return {operation}. - +- If {operationName} is {null}: + - If {document} contains exactly one operation. + - Return the Operation contained in the {document}. + - Otherwise raise a request error requiring {operationName}. +- Otherwise: + - Let {operation} be the Operation named {operationName} in {document}. + - If {operation} was not found, raise a request error. + - Return {operation}. ### Validating Requests @@ -55,91 +57,87 @@ 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. +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. - ### Coercing 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. +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. CoerceVariableValues(schema, operation, variableValues): - * Let {coercedValues} be an empty unordered Map. - * Let {variableDefinitions} be the variables defined by {operation}. - * For each {variableDefinition} in {variableDefinitions}: - * 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}. - * Return {coercedValues}. +- Let {coercedValues} be an empty unordered Map. +- Let {variableDefinitions} be the variables defined by {operation}. +- For each {variableDefinition} in {variableDefinitions}: + - 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}. +- Return {coercedValues}. Note: This algorithm is very similar to {CoerceArgumentValues()}. - ## Executing 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. +provide a query root operation type. If mutations or subscriptions are +supported, it must also provide a mutation or subscription root operation type, +respectively. ### Query 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. +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. ExecuteQuery(query, schema, variableValues, initialValue): - * 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}. - * Let {data} be the result of running - {ExecuteSelectionSet(selectionSet, queryType, initialValue, variableValues)} - *normally* (allowing parallelization). - * Let {errors} be any *field errors* produced while executing the - selection set. - * Return an unordered map containing {data} and {errors}. +- 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}. +- Let {data} be the result of running {ExecuteSelectionSet(selectionSet, + queryType, initialValue, variableValues)} _normally_ (allowing + parallelization). +- Let {errors} be any _field errors_ produced while executing the selection set. +- Return an unordered map containing {data} and {errors}. ### Mutation 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. +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 @@ -147,15 +145,13 @@ mutations ensures against race conditions during these side-effects. ExecuteMutation(mutation, schema, variableValues, initialValue): - * 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}. - * Let {data} be the result of running - {ExecuteSelectionSet(selectionSet, mutationType, initialValue, variableValues)} - *serially*. - * Let {errors} be any *field errors* produced while executing the - selection set. - * Return an unordered map containing {data} and {errors}. +- 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}. +- Let {data} be the result of running {ExecuteSelectionSet(selectionSet, + mutationType, initialValue, variableValues)} _serially_. +- Let {errors} be any _field errors_ produced while executing the selection set. +- Return an unordered map containing {data} and {errors}. ### Subscription @@ -168,9 +164,11 @@ that maps an underlying Source Stream to a returned Response Stream. Subscribe(subscription, schema, variableValues, initialValue): - * 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}. +- 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}. Note: In large scale subscription systems, the {Subscribe()} and {ExecuteSubscriptionEvent()} algorithms may be run on separate services to @@ -243,74 +241,79 @@ should be chosen by the implementing service. #### Source 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. +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. CreateSourceEventStream(subscription, schema, variableValues, initialValue): - * 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 {fields} be the value of the first entry in {groupedFieldSet}. - * Let {fieldName} be the name of the first entry in {fields}. - Note: This value is unaffected if an alias is used. - * Let {field} be the first entry in {fields}. - * 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 {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 {fields} be the value of the first entry in {groupedFieldSet}. +- Let {fieldName} be the name of the first entry in {fields}. Note: This value + is unaffected if an alias is used. +- Let {field} be the first entry in {fields}. +- Let {argumentValues} be the result of {CoerceArgumentValues(subscriptionType, + field, variableValues)} +- Let {fieldStream} be the result of running + {ResolveFieldEventStream(subscriptionType, initialValue, fieldName, + argumentValues)}. +- Return {fieldStream}. ResolveFieldEventStream(subscriptionType, rootValue, fieldName, argumentValues): - * 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}. -Note: This {ResolveFieldEventStream()} algorithm is intentionally similar -to {ResolveFieldValue()} to enable consistency when defining resolvers -on any operation type. +- 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}. + +Note: This {ResolveFieldEventStream()} algorithm is intentionally similar to +{ResolveFieldValue()} to enable consistency when defining resolvers on any +operation type. #### Response Stream -Each event in the underlying Source Stream triggers execution of the subscription -selection set using that event as a root value. +Each event in the underlying Source Stream triggers execution of the +subscription selection set using that event as a root value. MapSourceToResponseEvent(sourceStream, subscription, schema, variableValues): - * 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 {responseStream} completes: complete this event stream. +- 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 {responseStream} completes: complete this event stream. ExecuteSubscriptionEvent(subscription, schema, variableValues, initialValue): - * 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 {data} be the result of running - {ExecuteSelectionSet(selectionSet, subscriptionType, initialValue, variableValues)} - *normally* (allowing parallelization). - * Let {errors} be any *field errors* produced while executing the - selection set. - * Return an unordered map containing {data} and {errors}. +- 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 {data} be the result of running {ExecuteSelectionSet(selectionSet, + subscriptionType, initialValue, variableValues)} _normally_ (allowing + parallelization). +- Let {errors} be any _field errors_ produced while executing the selection set. +- Return an unordered map containing {data} and {errors}. Note: The {ExecuteSubscriptionEvent()} algorithm is intentionally similar to {ExecuteQuery()} since this is how each event result is produced. #### Unsubscribe -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 +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. Unsubscribe(responseStream): - * Cancel {responseStream} +- Cancel {responseStream} ## Executing Selection Sets @@ -319,22 +322,24 @@ need to be known, as well as whether it must be executed serially, or may be executed in parallel. First, the selection set is turned into a grouped field set; then, each -represented field in the grouped field set produces an entry into a -response map. +represented field in the grouped field set produces an entry into a response +map. ExecuteSelectionSet(selectionSet, objectType, objectValue, variableValues): - * Let {groupedFieldSet} be the result of - {CollectFields(objectType, selectionSet, variableValues)}. - * Initialize {resultMap} to an empty ordered map. - * 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} be {ExecuteField(objectType, objectValue, fieldType, fields, variableValues)}. - * Set {responseValue} as the value for {responseKey} in {resultMap}. - * Return {resultMap}. +- Let {groupedFieldSet} be the result of {CollectFields(objectType, + selectionSet, variableValues)}. +- Initialize {resultMap} to an empty ordered map. +- 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} be {ExecuteField(objectType, objectValue, fieldType, + fields, variableValues)}. + - Set {responseValue} as the value for {responseKey} in {resultMap}. +- Return {resultMap}. Note: {resultMap} is ordered by which fields appear first in the operation. This is explained in greater detail in the Field Collection section below. @@ -372,17 +377,17 @@ For example, given the following grouped field set to be executed normally: } ``` -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`). +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: +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 selection set to be executed serially: @@ -399,14 +404,14 @@ For example, given the following selection set to be executed serially: 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. +- 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: +`changeTheNumber` that returns an object containing one field, `theNumber`. If +we execute the following selection set serially: ```graphql example { @@ -424,12 +429,12 @@ As an illustrative example, let's assume we have a mutation field The executor will execute the following serially: - - Resolve the `changeTheNumber(newNumber: 1)` field - - Execute the `{ theNumber }` sub-selection set of `first` normally - - Resolve the `changeTheNumber(newNumber: 3)` field - - Execute the `{ theNumber }` sub-selection set of `second` normally - - Resolve the `changeTheNumber(newNumber: 2)` field - - Execute the `{ theNumber }` sub-selection set of `third` normally +- Resolve the `changeTheNumber(newNumber: 1)` field +- Execute the `{ theNumber }` sub-selection set of `first` normally +- Resolve the `changeTheNumber(newNumber: 3)` field +- Execute the `{ theNumber }` sub-selection set of `second` normally +- Resolve the `changeTheNumber(newNumber: 2)` field +- Execute the `{ theNumber }` sub-selection set of `third` normally A correct executor must generate the following result for that selection set: @@ -447,14 +452,13 @@ A correct executor must generate the following result for that selection set: } ``` - ### Field 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. +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`: @@ -481,66 +485,79 @@ response in a stable and predictable order. CollectFields(objectType, selectionSet, variableValues, visitedFragments): - * If {visitedFragments} is not provided, initialize it to the empty set. - * Initialize {groupedFields} to an empty ordered map of lists. - * 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 +- If {visitedFragments} is not provided, initialize it to the empty set. +- Initialize {groupedFields} to an empty ordered map of lists. +- 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} 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 + - If {selection} is a {Field}: + - Let {responseKey} be the response key of {selection} (the alias if + defined, otherwise the field name). + - Let {groupForResponseKey} be the list in {groupedFields} for + {responseKey}; if no such list exists, create it as an empty list. + - Append {selection} to the {groupForResponseKey}. + - If {selection} is a {FragmentSpread}: + - Let {fragmentSpreadName} be the name of {selection}. + - If {fragmentSpreadName} is in {visitedFragments}, 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 {groupForResponseKey} be the list in {groupedFields} for + - 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}. + - Let {fragmentGroupedFieldSet} be the result of calling + {CollectFields(objectType, fragmentSelectionSet, variableValues, + 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}. + - 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}. + - Let {fragmentGroupedFieldSet} be the result of calling + {CollectFields(objectType, fragmentSelectionSet, variableValues, + 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 {selection} to the {groupForResponseKey}. - * If {selection} is a {FragmentSpread}: - * Let {fragmentSpreadName} be the name of {selection}. - * 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}. - * Let {fragmentGroupedFieldSet} be the result of calling - {CollectFields(objectType, fragmentSelectionSet, variableValues, 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}. - * 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}. - * Let {fragmentGroupedFieldSet} be the result of calling {CollectFields(objectType, fragmentSelectionSet, variableValues, 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}. - * Return {groupedFields}. + - Append all items in {fragmentGroup} to {groupForResponseKey}. +- Return {groupedFields}. DoesFragmentTypeApply(objectType, fragmentType): - * 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}. +- 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}. Note: The steps in {CollectFields()} evaluating the `@skip` and `@include` directives may be applied in either order since they apply commutatively. - ## Executing Fields Each field requested in the grouped field set that is defined on the selected @@ -550,12 +567,15 @@ finally completes that value either by recursively executing another selection set or coercing a scalar value. ExecuteField(objectType, objectValue, fieldType, fields, variableValues): - * Let {field} be the first entry in {fields}. - * Let {fieldName} be the field name of {field}. - * 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)}. +- Let {field} be the first entry in {fields}. +- Let {fieldName} be the field name of {field}. +- 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)}. ### Coercing Field Arguments @@ -567,104 +587,108 @@ At each argument position in an operation may be a literal {Value}, or a {Variable} to be provided at runtime. CoerceArgumentValues(objectType, field, variableValues): - * 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}. - * Return {coercedValues}. + +- 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}. +- Return {coercedValues}. Note: Variable values are not coerced because they are expected to be coerced before executing the operation in {CoerceVariableValues()}, and valid operations must only allow usage of variables of appropriate types. - ### Value 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. +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. ResolveFieldValue(objectType, objectValue, fieldName, argumentValues): - * 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}. + +- 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}. Note: It is common for {resolver} to be asynchronous due to relying on reading an underlying database or networked service to produce a value. This -necessitates the rest of a GraphQL executor to handle an asynchronous -execution flow. - +necessitates the rest of a GraphQL executor to handle an asynchronous execution +flow. ### Value 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. +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. CompleteValue(fieldType, fields, result, variableValues): - * If the {fieldType} is a Non-Null type: - * Let {innerType} be the inner type of {fieldType}. - * Let {completedResult} be the result of calling - {CompleteValue(innerType, fields, result, variableValues)}. - * If {completedResult} is {null}, raise a field error. - * Return {completedResult}. - * 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 a list where each list item is the result of calling - {CompleteValue(innerType, fields, resultItem, variableValues)}, where - {resultItem} is each item in {result}. - * 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 {subSelectionSet} be the result of calling {MergeSelectionSets(fields)}. - * Return the result of evaluating {ExecuteSelectionSet(subSelectionSet, objectType, result, variableValues)} *normally* (allowing for parallelization). + +- If the {fieldType} is a Non-Null type: + - Let {innerType} be the inner type of {fieldType}. + - Let {completedResult} be the result of calling {CompleteValue(innerType, + fields, result, variableValues)}. + - If {completedResult} is {null}, raise a field error. + - Return {completedResult}. +- 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 a list where each list item is the result of calling + {CompleteValue(innerType, fields, resultItem, variableValues)}, where + {resultItem} is each item in {result}. +- 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 {subSelectionSet} be the result of calling {MergeSelectionSets(fields)}. + - Return the result of evaluating {ExecuteSelectionSet(subSelectionSet, + objectType, result, variableValues)} _normally_ (allowing for + parallelization). **Coercing Results** @@ -673,16 +697,18 @@ 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](#sec-Scalars.Result-Coercion-and-Serialization) +See the Scalars +[Result Coercion and Serialization](#sec-Scalars.Result-Coercion-and-Serialization) sub-section for more detailed information about how GraphQL's built-in scalars coerce result values. CoerceResult(leafType, value): - * 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 throw a field error. + +- 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 throw a field error. Note: If a field resolver returns {null} then it is handled within {CompleteValue()} before {CoerceResult()} is called. Therefore both the input @@ -692,17 +718,18 @@ and output of {CoerceResult()} must not be {null}. 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. +type. This determination is made by the internal system using whatever means +appropriate. Note: A common method of determining the Object type for an {objectValue} in object-oriented environments, such as Java or C#, is to use the class name of the {objectValue}. ResolveAbstractType(abstractType, objectValue): - * Return the result of calling the internal method provided by the type - system for determining the Object type of {abstractType} given the - value {objectValue}. + +- 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** @@ -728,13 +755,13 @@ After resolving the value for `me`, the selection sets are merged together so `firstName` and `lastName` can be resolved for one value. MergeSelectionSets(fields): - * Let {selectionSet} be an empty list. - * For each {field} in {fields}: - * Let {fieldSelectionSet} be the selection set of {field}. - * If {fieldSelectionSet} is null or empty, continue to the next field. - * Append all selections in {fieldSelectionSet} to {selectionSet}. - * Return {selectionSet}. +- Let {selectionSet} be an empty list. +- For each {field} in {fields}: + - Let {fieldSelectionSet} be the selection set of {field}. + - If {fieldSelectionSet} is null or empty, continue to the next field. + - Append all selections in {fieldSelectionSet} to {selectionSet}. +- Return {selectionSet}. ### Handling Field Errors @@ -747,13 +774,13 @@ are raised before execution begins. If a request error is encountered, execution does not begin and no data is returned in the 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. +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. +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 @@ -766,10 +793,10 @@ 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. +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}. +return `Non-Null` types, then the {"data"} entry in the response should be +{null}. diff --git a/spec/Section 7 -- Response.md b/spec/Section 7 -- Response.md index 071eb1058..8ddaaebf6 100644 --- a/spec/Section 7 -- Response.md +++ b/spec/Section 7 -- Response.md @@ -11,19 +11,19 @@ the case that a field error was raised on a field and was replaced with {null}. A response to a GraphQL request must be a map. -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. - -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 implementors -to extend the protocol however they see fit, and hence there are no additional +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. + +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 implementors to +extend the protocol however they see fit, and hence there are no additional restrictions on its contents. To ensure future changes to the protocol do not break existing services and @@ -31,34 +31,33 @@ clients, the top level response map must not contain any entries other than the three described above. Note: When `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. +appear first when serialized to make it more clear when errors are present in a +response during debugging. ### Data 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. +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`. +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`. ### Errors The `errors` entry in the response is a non-empty list of errors, where each error is a map. -If no errors were raised during the request, the `errors` entry should -not be present in the result. +If no errors were raised during the request, the `errors` entry should not be +present in the result. -If the `data` entry in the response is not present, the `errors` -entry in the response must not be empty. It must contain at least one error. -The errors it contains should indicate why no data was able to be returned. +If the `data` entry in the response is not present, the `errors` entry in the +response must not be empty. It must contain at least one error. The errors it +contains should indicate 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 in the response may contain any field errors that @@ -85,33 +84,33 @@ resulting value. Field errors are typically the fault of GraphQL service. If a field error is raised, execution attempts to continue and a partial result -is produced (see [Handling Field Errors](#sec-Handling-Field-Errors)). -The `data` entry in the response must be present. The `errors` entry should -include all raised field errors. +is produced (see [Handling Field Errors](#sec-Handling-Field-Errors)). The +`data` entry in the response must be present. The `errors` entry should include +all raised field errors. **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. +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. +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. +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. This field should be a list of path segments starting at the root of the -response and ending with the field associated with the error. Path segments -that represent fields should be strings, and path segments that -represent list indices should be 0-indexed integers. If the error happens -in an aliased field, the path to the error should use the aliased name, since -it represents a path in the response, not in the request. +response and ending with the field associated with the error. Path segments that +represent fields should be strings, and path segments that represent list +indices should be 0-indexed integers. If the error happens in an aliased field, +the path to the error should use the aliased name, since it represents a path in +the response, not in the request. For example, if fetching one of the friends' names fails in the following operation: @@ -162,9 +161,9 @@ The response might look like: ``` 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. +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 @@ -198,10 +197,10 @@ be the same: } ``` -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 -implementors to add additional information to errors however they see fit, and -there are no additional restrictions on its contents. +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 implementors to add additional information to errors however they +see fit, and there are no additional restrictions on its contents. ```json example { @@ -223,9 +222,9 @@ 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. -Note: Previous versions of this spec did not describe the `extensions` entry -for error formatting. While non-specified entries are not violations, they are -still discouraged. +Note: Previous versions of this spec did not describe the `extensions` entry for +error formatting. While non-specified entries are not violations, they are still +discouraged. ```json counter-example { @@ -241,7 +240,6 @@ still discouraged. } ``` - ## Serialization Format GraphQL does not require a specific serialization format. However, clients @@ -249,26 +247,25 @@ 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 +- 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 +- 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. - ### JSON Serialization JSON is the most common serialization format for GraphQL. Though as mentioned @@ -291,7 +288,6 @@ values should be used to encode the related GraphQL values: Note: For consistency and ease of notation, examples of responses are given in JSON format throughout this document. - ### Serialized Map Ordering Since the result of evaluating a selection set is ordered, the serialized Map of @@ -299,14 +295,14 @@ 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. +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