From 7c0398803c94826957be307e1d3c4a5ffc3c5b5e Mon Sep 17 00:00:00 2001 From: Lee Byron Date: Tue, 30 Jun 2015 17:41:15 -0700 Subject: [PATCH] GraphQL Specification, Working Draft This is the first public commit in what has been a few months of internal development. Future development will occur in public directly in this repository. --- .gitignore | 8 + GraphQL.md | 57 ++ README.md | 1081 +++++++++++++++++++++++++++++ Section 1 -- Overview.md | 85 +++ Section 2 -- Language.md | 450 +++++++++++++ Section 3 -- Type System.md | 766 +++++++++++++++++++++ Section 4 -- Introspection.md | 334 +++++++++ Section 5 -- Validation.md | 1197 +++++++++++++++++++++++++++++++++ Section 6 -- Execution.md | 296 ++++++++ Section 7 -- Response.md | 115 ++++ Section 8 -- Grammar.md | 362 ++++++++++ package.json | 25 + 12 files changed, 4776 insertions(+) create mode 100644 .gitignore create mode 100644 GraphQL.md create mode 100644 README.md create mode 100644 Section 1 -- Overview.md create mode 100644 Section 2 -- Language.md create mode 100644 Section 3 -- Type System.md create mode 100644 Section 4 -- Introspection.md create mode 100644 Section 5 -- Validation.md create mode 100644 Section 6 -- Execution.md create mode 100644 Section 7 -- Response.md create mode 100644 Section 8 -- Grammar.md create mode 100644 package.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..3eddd41e3 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*.swp +*~ +.*.haste_cache.* +.DS_Store +build +out +node_modules +npm-debug.log diff --git a/GraphQL.md b/GraphQL.md new file mode 100644 index 000000000..1805292b0 --- /dev/null +++ b/GraphQL.md @@ -0,0 +1,57 @@ +GraphQL +------- + +*Working Draft - July 2015* + +**Introduction** + +This is a Draft RFC Specification for GraphQL, a query language created by +Facebook in 2012 for describing the capabilities and requirements of data models +for client-server applications. The development of this standard started +in 2015. GraphQL is a new and evolving language and is not complete. Significant +enhancement will continue in future editions of this specification. + +**Copyright notice** + +Copyright (c) 2015, Facebook, Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + + * Neither the name Facebook nor the names of its contributors may be used to + endorse or promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# [Overview](Section 1 -- Overview.md) + +# [Language](Section 2 -- Language.md) + +# [Type System](Section 3 -- Type System.md) + +# [Introspection](Section 4 -- Introspection.md) + +# [Validation](Section 5 -- Validation.md) + +# [Execution](Section 6 -- Execution.md) + +# [Response](Section 7 -- Response.md) + +# [Grammar](Section 8 -- Grammar.md) diff --git a/README.md b/README.md new file mode 100644 index 000000000..d95c964ec --- /dev/null +++ b/README.md @@ -0,0 +1,1081 @@ +# GraphQL + +This is a Working Draft of the Specification for GraphQL, a query language +created by Facebook for describing data requirements on complex application +data models. + +## Technical Preview Contents + +This technical preview contains a draft specification for GraphQL and a reference +implementation in JavaScript that implements that draft, +[GraphQL.js](https://github.com/graphql/graphql-js). + +The reference implemention provides base libraries in javascript that would +provide the basis for full GraphQL implementations and tools. It is not a fully +standalone GraphQL server that a client developer could use to start +manipulating and querying data. Most importantly it provides no mapping to a +functioning, production-ready backend. The only “backend” we have targeted for +this early preview are in-memory stubs in test cases. + +We are releasing this now because after GraphQL was first discussed publically, +many engineers used this information to implement the parts of the system that +we discussed publically. We want to support those engineers by providing both a +formal specification and a reference implementation for the system as a whole. + +To that end the target audience is not the client developer, but those who have +or are actively interested in building their own GraphQL implementations and +tools. Critically we also want feedback on system and to incorporate that +feedback in our final release. + +In order to be broadly adopted GraphQL will have to target a wide +variety of backends, frameworks, and languages, which will necessitate a +collaborative effort across projects and organizations. This technical preview +marks the beginning of that process. + +## Getting Started + +GraphQL consists of a type system, query language and execution semantics, +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. + +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. + +### 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. + +For our Star Wars example, the +[starWarsSchema.js](https://github.com/graphql/graphql-js/blob/master/src/__tests__/starWarsSchema.js) +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: + +``` +type Human { + name: String +} +``` + +This shorthand is convinient 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/master/src/__tests__/starWarsData.js), +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. + +``` +type Human { + id: String + name: String + homePlanet: String +} +``` + +Since we're talking about the Star Wars trilogy, it would be useful +to describe what episodes each character appears in. To do so, we'll +first define an enum, which lists the three episodes in the trilogy: + +``` +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: + +``` +type Human { + id: String + name: String + appearsIn: [Episode] + homePlanet: String +} +``` + +Now, let's introduce another type, `Droid`: + + +``` +type Droid { + id: String + name: String + appearsIn: [Episode] + primaryFunction: String +} +``` + +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 +they appear in. 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: + +``` +enum Episode { NEWHOPE, EMPIRE, JEDI } + +interface Character { + id: String + name: String + friends: [Character] + appearsIn: [Episode] +} + +type Human : Character { + id: String + name: String + friends: [Character] + appearsIn: [Episode] + homePlanet: String +} + +type Droid : Character { + id: String + name: String + friends: [Character] + appearsIn: [Episode] + primaryFunction: String +} +``` + +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 fulfull 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 non-nullable, we allow +ourselves the flexability to eventually return null to indicate a backend +error, while also telling the client that the error occurred. + +``` +enum Episode { NEWHOPE, EMPIRE, JEDI } + +interface Character { + id: String! + name: String + friends: [Character] + appearsIn: [Episode] +} + +type Human : Character { + id: String! + name: String + friends: [Character] + appearsIn: [Episode] + homePlanet: String +} + +type Droid : Character { + id: String! + name: String + friends: [Character] + appearsIn: [Episode] + primaryFunction: String +} +``` + +We're missing one last piece: an entry point into the type system. So we'll +define one more type that allows us to get to these objects: + +``` +type Query { + hero: Character + human(id: String!): Human + droid(id: String!): Droid +} +``` + +This type will be the entry point for the queries described below; it has +two fields. `hero` returns the `Character` who is the hero of the +Star Wars trilogy. `human` has a new feature in the type system, a query +argument. This field 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. + +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/master/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. + +For our Star Wars example, the +[starWarsQueryTests.js](https://github.com/graphql/graphql-js/blob/master/src/__tests__/starWarsQueryTests.js) +file in the GraphQL.js repository contains a number of queries and responses. +That file is a test file that uses the schema discussed above and a set of +sample data, located in +[starWarsData.js](https://github.com/graphql/graphql-js/blob/master/src/__tests__/starWarsData.js). +This test file can be run to exercise the reference implementation. + +An example query on the above schema would be: + +``` +query HeroNameQuery { + hero { + name + } +} +``` + +The initial line, `query HeroNameQuery`, says that we're running a query, +and tells GraphQL to start 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: + +```json +{ + "hero": { + "name": "R2-D2" + } +} +``` + +Assuming that the backing data for the GraphQL server identified R2-D2 as the +hero. The response continues to vary based on the request; if we asked for +R2-D2s ID and friends with this query: + +``` +query HeroNameAndFriendsQuery { + hero { + id + name + friends { + id + name + } + } +} +``` + +then we'll get back a response like this: + +```json +{ + "hero": { + "id": "2001", + "name": "R2-D2", + "friends": [ + { + "id": "1000", + "name": "Luke Skywalker" + }, + { + "id": "1002", + "name": "Han Solo" + }, + { + "id": "1003", + "name": "Leia Organa" + } + ] + } +} +``` + +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-D2s +friends, gets their name and episode appearances, then ask for each of *their* +friends. + +``` +query NestedQuery { + hero { + name + friends { + name + appearsIn + friends { + name + } + } + } +} +``` + +which will give us the nested response + +```json +{ + "hero": { + "name": "R2-D2", + "friends": [ + { + "name": "Luke Skywalker", + "appearsIn": [ "NEWHOPE", "EMPIRE", "JEDI" ], + "friends": [ + { "name": "Han Solo" }, + { "name": "Leia Organa" }, + { "name": "C-3PO" }, + { "name": "R2-D2" } + ] + }, + { + "name": "Han Solo", + "appearsIn": [ "NEWHOPE", "EMPIRE", "JEDI" ], + "friends": [ + { "name": "Luke Skywalker" }, + { "name": "Leia Organa" }, + { "name": "R2-D2" } + ] + }, + { + "name": "Leia Organa", + "appearsIn": [ "NEWHOPE", "EMPIRE", "JEDI" ], + "friends": [ + { "name": "Luke Skywalker" }, + { "name": "Han Solo" }, + { "name": "C-3PO" }, + { "name": "R2-D2" } + ] + } + ] + } +} +``` + +The `Query` type above defined a way to fetch a human given their +ID. We can use it by hardcoding the ID in the query: + +``` +query FetchLukeQuery { + human(id: "1000") { + name + } +} +``` + +to get + +```json +{ + "human": { + "name": "Luke Skywalker" + } +} +``` + +Alternately, we could have defined the query to have a query parameter: + +``` +query FetchSomeIDQuery($someId: String!) { + human(id: $someId) { + name + } +} +``` + +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. + +We can do that with field aliases, as demonstrated in this query: + +``` +query FetchLukeAliased { + luke: human(id: "1000") { + name + } +} +``` + +We aliased the result of the `human` field to the key `luke`. Now the response +is: + +```json +{ + "luke": { + "name": "Luke Skywalker" + }, +} +``` + +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 argumetns, as in the following query: + +``` +query FetchLukeAndLeiaAliased { + luke: human(id: "1000") { + name + } + leia: human(id: "1003") { + name + } +} +``` + +We aliased the result of the first `human` field to the key +`luke`, and the second to `leia`. So the result will be: + +```json +{ + "luke": { + "name": "Luke Skywalker" + }, + "leia": { + "name": "Leia Organa" + } +} +``` + +Now imagine we wanted to ask for Luke and Leia's home planets. We could do so +with this query: + +``` +query DuplicateFields { + luke: human(id: "1000") { + name + homePlanet + } + leia: human(id: "1003") { + name + homePlanet + } +} +``` + +but we can already see that this could get unwieldy, since we have to add new +fields to both parts of the query. Instead, we can extract out the common fields +into a fragment, and include the fragment in the query, like this: + +``` +query UseFragment { + luke: human(id: "1000") { + ...HumanFragment + } + leia: human(id: "1003") { + ...HumanFragment + } +} + +fragment HumanFragment on Human { + name + homePlanet +} +``` + +Both of those queries give this result: + +```json +{ + "luke": { + "name": "Luke Skywalker", + "homePlanet": "Tatooine" + }, + "leia": { + "name": "Leia Organa", + "homePlanet": "Alderaan" + } +} +``` + +The `UseFragment` and `DuplicateFields` queries will got the same result, but +the latter has less copy paste; 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. + +``` +query CheckTypeOfR2 { + hero { + __typename + name + } +} +``` + +Since R2-D2 is a droid, this will return + +```json +{ + "hero": { + "__typename": "Droid", + "name": "R2-D2" + } +} +``` + +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/master/src/language) +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 pre-determined 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/master/src/__tests__/starWarsValidationTests.js) +contains a number of queries demonstrating various invalidities, 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: + +``` +query NestedQueryWithFragment { + hero { + ...NameAndAppearances + friends { + ...NameAndAppearances + friends { + ...NameAndAppearances + } + } + } +} + +fragment NameAndAppearances on Character { + name + appearsIn +} +``` + +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: + +``` +# INVALID: favoriteSpaceship does not exist on Character +query HeroSpaceshipQuery { + hero { + favoriteSpaceship + } +} +``` + +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 +`appearsIn` on it; if we omit that, the query will not be valid: + +``` +# INVALID: hero is not a scalar, so fields are needed +query HeroNoFieldsQuery { + hero +} +``` + +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: + +``` +# INVALID: name is a scalar, so fields are not permitted +query HeroFieldsOnScalarQuery { + hero { + name { + firstCharacterOfName + } + } +} +``` + +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? + +``` +# INVALID: primaryFunction does not exist on Character +query DroidFieldOnCharacter { + hero { + name + primaryFunction + } +} +``` + +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` +where it is defined. + +``` +query DroidFieldInFragment { + hero { + name + ...DroidFields + } +} + +fragment DroidFields on Droid { + primaryFunction +} +``` + +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, then, we can use an inline fragment; this +still allows us to indicate the type we are querying on, but without naming +a separate fragment: + +``` +query DroidFieldInInlineFragment { + hero { + name + ... on Droid { + primaryFunction + } + } +} +``` + +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 +[validator](https://github.com/graphql/graphql-js/blob/master/src/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! + +For our Star Wars example, the file +[starWarsIntrospectionTests.js](https://github.com/graphql/graphql-js/blob/master/src/__tests__/starWarsIntrospectionTests.js) +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. + +``` +query IntrospectionTypeQuery { + __schema { + types { + name + } + } +} +``` + +and we get back: + +```json +{ + "__schema": { + "types": [ + { + "name": "Query" + }, + { + "name": "Character" + }, + { + "name": "Human" + }, + { + "name": "String" + }, + { + "name": "Episode" + }, + { + "name": "Droid" + }, + { + "name": "__Schema" + }, + { + "name": "__Type" + }, + { + "name": "__TypeKind" + }, + { + "name": "Boolean" + }, + { + "name": "__Field" + }, + { + "name": "__InputValue" + }, + { + "name": "__EnumValue" + }, + { + "name": "__Directive" + } + ] + } +} +``` + +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. + +Now, let's try and figure out a good place to start exploring what queries are +available. When we designed out type system, we specified what type all queries +would start at; let's ask the introspection system about that! + +``` +query IntrospectionQueryTypeQuery { + __schema { + queryType { + name + } + } +} +``` + +and we get back: + +```json +"__schema": { + "query": { + "name": "Query" + }, +} +``` + +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 had we specified it +was 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: + + +``` +query IntrospectionDroidTypeQuery { + __type(name: "Droid") { + name + } +} +``` + +and we get back: + +```json +"__type": { + "name": "Droid" +} +``` + +What if we want to know more about Droid, though? For example, is it +an interface or an object? + +``` +query IntrospectionDroidKindQuery { + __type(name: "Droid") { + name + kind + } +} +``` + +and we get back: + +```json +"__type": { + "name": "Droid", + "kind": "OBJECT" +} +``` + +`kind` returns a `__TypeKind` enum, one of whose values is `OBJECT`. If +we asked about `Character` instead: + + +``` +query IntrospectionCharacterKindQuery { + __type(name: "Character") { + name + kind + } +} +``` + +and we get back: + +```json +"__type": { + "name": "Character", + "kind": "INTERFACE" +} +``` + +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`: + +``` +query IntrospectionDroidFieldsQuery { + __type(name: "Droid") { + name + fields { + name + type { + name + kind + } + } + } +} +``` + +and we get back: + +```json +{ + "__type": { + "name": "Droid", + "fields": [ + { + "name": "id", + "type": { + "name": null, + "kind": "NON_NULL" + } + } + { + "name": "name", + "type": { + "name": "String", + "kind": "SCALAR" + } + } + { + "name": "friends", + "type": { + "name": null, + "kind": "LIST" + } + } + { + "name": "appearsIn", + "type": { + "name": null, + "kind": "LIST" + } + } + { + "name": "primaryFunction", + "type": { + "name": "String", + "kind": "SCALAR" + } + } + ] + } +} +``` + +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. + +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. + +``` +query IntrospectionDroidWrappedFieldsQuery { + __type(name: "Droid") { + name + fields { + name + type { + name + kind + ofType { + name + kind + } + } + } + } +} +``` + +and we get back: + +```json +{ + "__type": { + "name": "Droid", + "fields": [ + { + "name": "id", + "type": { + "name": null, + "kind": "NON_NULL", + "ofType": { + "name": "String", + "kind": "SCALAR" + } + } + } + { + "name": "name", + "type": { + "name": "String", + "kind": "SCALAR", + "ofType": null + } + } + { + "name": "friends", + "type": { + "name": null, + "kind": "LIST", + "ofType": { + "name": "Character", + "kind": "INTERFACE" + } + } + } + { + "name": "appearsIn", + "type": { + "name": null, + "kind": "LIST", + "ofType": { + "name": "Episode", + "kind": "ENUM" + } + } + } + { + "name": "primaryFunction", + "type": { + "name": "String", + "kind": "SCALAR", + "ofType": null + } + } + ] + } +} +``` + +Let's end with a feature of the introspection system particularly useful +for tooling; let's ask the system for documentation! + +``` +query IntrospectionDroidDescriptionQuery { + __type(name: "Droid") { + name + description + } +} +``` + +yields + +```json +{ + "__type": { + "name": "Droid", + "description": "A mechanical creature in the Star Wars universe." + } +} +``` + +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/master/src/type/introspection.js) +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, +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 a grammar for GraphQL. diff --git a/Section 1 -- Overview.md b/Section 1 -- Overview.md new file mode 100644 index 000000000..a206c2485 --- /dev/null +++ b/Section 1 -- Overview.md @@ -0,0 +1,85 @@ +# Overview + +GraphQL is a query language designed to build client applications by providing +an intuitive and flexible syntax and system for describing their data +requirements and interactions. + +For example, this GraphQL request will receive the name of the user with id 4 +from the Facebook implementation of GraphQL. + +```graphql +{ + user(id: 4) { + name + } +} +``` + +Which produces the resulting data (in JSON): + +```js +{ + "user": { + "name": "Mark Zuckerberg" + } +} +``` + +GraphQL is not a programming language capable of arbitrary computation, but is +instead a language used to query application servers that have +capabilities defined in this specification. GraphQL does not mandate a +particular programming language or storage system for application servers that +implement it. Instead, application servers take their capabilities and map them +to a uniform language, type system, and philosophy that GraphQL encodes. +This provides a unified interface friendly to product development and a powerful +platform for tool-building. + +GraphQL has a number of design principles: + + * **Hierarchal**: Most product development today involves the creation and + manipulation of view hierarchies. To achieve congruence with the structure + of these applications, a GraphQL query itself is structured hierarchically. + The query is shaped just like the data it returns. It is a natural + way for clients to describe data requirements. + + * **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 build the language and runtime + necessary to enable that. + + * **Strong-typing**: Every GraphQL server defines an application-specific + type system. Queries are executed within the context of that type system. + Given a query, tools can ensure that the query is both syntactically + correct and valid within the GraphQL type system before execution, i.e. at + development time, and the server can make certain guarantees about the shape + and nature of the response. + + * **Client-specified queries**: Through its type system, a GraphQL server + 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 queries are specified at field-level + granularity. In the majority of client-server applications written + without GraphQL, the server determines the data returned in its various + scripted endpoints. A GraphQL query, on the other hand, returns exactly what + a client asks for and no more. + + * **Introspective**: GraphQL is introspective. A GraphQL server's type system + must be queryable by the GraphQL language itself, as will be described in this + specification. GraphQL introspection serves as a powerful platform for + building common tools and client software libraries. + +Because of these principles, GraphQL is a powerful and productive environment +for building client applications. Product developers and designers building +applications against working GraphQL servers -- supported with quality tools -- +can quickly become productive without reading extensive documentation and with +little or no formal training. To enable that experience, there must be those +that build those servers 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 server 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/Section 2 -- Language.md b/Section 2 -- Language.md new file mode 100644 index 000000000..09ba81a29 --- /dev/null +++ b/Section 2 -- Language.md @@ -0,0 +1,450 @@ +# Language + +Clients use the GraphQL language to make requests from a GraphQL server. We +refer to these requests as documents. A document may contain operations +(queries and mutations are both operations) and fragments, a common unit +of composition allowing for query reuse. + +## Document + +GraphQL documents are only executable by a server if they contain an operation. +However documents which do not contain operations may still be parsed and +validated to allow client to represent a single request across many documents. + +GraphQL documents may contain multiple operations, as long as they are named. +When submitting a document with multiple operations to a GraphQL server, the +name of the desired operation must also be provided. + +If a document contains only one query operation, that operation may be +represented in the shorthand form, which omits the query keyword and +query name. + +## Operations + +There are two types of operations that GraphQL models: + + * query - a read-only fetch. + * mutation - a write followed by a fetch. + +Each operation is represented by a custom name and a selection of fields. + +**Query shorthand** + +If a query has no variables or directives or name, the `query` keyword can be +omitted. This means it must be the only query in the document. + +Note: many examples below will use the query shorthand syntax. + +## Fields + +A field in the top-level selection set often represents some kind of +information that is globally accessible to your application and the current +viewer. Some typical examples of global fields: + +```graphql +# `me` could represent the currently logged in user. +query getMe { + me { /* ... */ } +} + +# `user` represents one of many users in a graph of data. +query getZuck { + user(id: 4) { /* ... */ } +} +``` + +## Field Selections + +Each field is of a specific type, and the sub-fields must always be explicitly +declared via a field selection, unless it is a scalar. For example, when +fetching data from some user object: + +```graphql +query getZuck { + user(id: 4) { + id, + firstName, + lastName + } +} +``` + +Field selections can be further composed to explicitly state all subfields of +nested types. All queries must specify down to scalar fields. + +```graphql +query getZuck { + user(id: 4) { + id, + firstName, + lastName, + birthday { + month, + day + } + } +} +``` + +## Field Arguments + +Fields may take arguments. These often map directly to function arguments +within the GraphQL server implementation. We already saw arguments used +in the global field above. + +In this example, we want to query a user's profile picture of a +specific size: + +```graphql +{ + user(id: 4) { + id, + name, + profilePic(size: 100) + } +} +``` + +Many arguments can exist for a given field: + +```graphql +{ + user(id: 4) { + id, + name, + profilePic(width: 100, height: 50) + } +} +``` + +**Arguments are unordered** + +Field arguments may be provided in any syntactic order and maintain identical +semantic meaning. + +These two queries are semantically identical: + +```graphql +{ + picture(width: 200, height: 100) +} +``` + +```graphql +{ + picture(height: 100, width: 200) +} +``` + +## Field Alias + +By default, the key in the response object will use the field name +queried. However, you can define a different name by specifying an alias. + +In this example, we can fetch two profile pictures of different sizes and ensure +the resulting object will not have duplicate keys: + +```graphql +{ + user(id: 4) { + id, + name, + smallPic: profilePic(size: 64), + bigPic: profilePic(size: 1024) + } +} +``` + +Which returns the result: + +```js +{ + "user": { + "id": 4, + "name": "Mark", + "smallPic": "https://cdn.site.io/pic-4-64.jpg", + "bigPic": "https://cdn.site.io/pic-4-1024.jpg" + } +} +``` + +Since the top level of a query is a field, it also can be given an alias: + +```graphql +{ + zuck: user(id: 4) { + id + name + } +} +``` + +Returns the result: + +```js +{ + "zuck": { + "id": 4, + "name": "Mark Zuckerberg" + } +} +``` + +A field's response key is its alias if an alias is provided, and it is +the field's name otherwise. + +## Input Values + +Both field arguments and directives accept input values. Input values can be +specified as a variable or respresented inline as literals. Input values can +be scalars, enumerations, or input objects. List and inputs objects may also +contain variables. + +**Int** + +Int is a number specified without a decimal point (ex. `1`). + +**Float** + +A Float numbers always includes a decimal point (ex. `1.0`) and may optionally +also include an exponent (ex. `6.0221413e23`). + +**Boolean** + +The two keywords `true` and `false` represent the two boolean values. + +**String** + +Strings are lists of characters wrapped in double-quotes `"`. (ex. `"Hello World"`). +Whitespace is significant within a string. + +**Enum Value** + +Enum values are respresented as unquoted names (ex. `MOBILE_WEB`). It is +recommended that Enum values be "all caps". Enum values are only used in contexts +where the precise enumeration type is known. Therefore it's not necessary +to use the enumeration type name in the literal. + +**List** + +Lists are an ordered sequence of values wrapped in square-brackets `[ ]`. The +values of an Array 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. + +**Input Object** + +Input object literals are un-ordered lists of keyed input values wrapped in +curly-braces `{ }`. The values of an object literal may be any input value +literal or variable (ex. `{ name: "Hello world", score: 1.0 }`). We refer to +literal representation of input objects as "object literals." + +## Variables + +A GraphQL query can be parameterized with variables, maximizing query reuse, +and avoiding costly string building in clients at runtime. + +Variables must be defined at the top of an operation and have global scope. + +In this example, we want to fetch a profile picture size based on the size +of a particular device: + +```graphql +query getZuckProfile($devicePicSize: Int) { + user(id: 4) { + id, + name, + profilePic(size: $devicePicSize) + } +} +``` + +Values for those variables are provided along with a GraphQL query, so they may be +substituted during execution. If providing JSON for the variables values, we +could run this query and request profilePic of size 60 with: + +```js +{ + "devicePicSize": 60 +} +``` + +## Directives + +In some cases, you need to provide options to alter GraphQL's execution +behavior in ways field arguments will not suffice, such as conditionally +skipping a field. Directives provide this with a `@name` and can be +specified to be used without an argument or with a value argument. + +Directives can be used to conditionally include fields in a query based +on a provided boolean value. In this contrived example experimentalField +will be queried and controlField will not. + +```graphql +query myQuery($someTest: Boolean) { + experimentalField @if: $someTest + controlField @unless: $someTest +} +``` + +As future versions of GraphQL adopts new configurable execution capabilities, +they may be exposed via directives. + +## Fragments + +Fragments allow for reuse of repeated portions of a query. It is the unit of +composition in GraphQL. + +For example, if we wanted to fetch some common information about mutual friends +as well as friends of some user: + +```graphql +query noFragments +{ + user(id: 4) { + friends(first: 10) { + id + name + profilePic(size: 50) + } + mutualFriends(first: 10) { + id + name + profilePic(size: 50) + } + } +} +``` + +The repeated fields could be extracted into a fragment and composed by +a parent fragment or query. + +```graphql +query withFragments { + user(id: 4) { + friends(first: 10) { ...friendFields } + mutualFriends(first: 10) { ...friendFields } + } +} + +fragment friendFields on User { + id + name + profilePic(size: 50) +} +``` + +Fragments are consumed by using the spread operator (`...`). All fields selected +by the fragment will be added to the query field selection at the same level +as the fragment invocation. This happens through multiple levels of fragment +spreads. + +For example: + +```graphql +query withNestedFragments +{ + user(id: 4) { + friends(first: 10) { ...friendFields } + mutualFriends(first: 10) { ...friendFields } + } +} + +fragment friendFields on User { + id + name + ..standardProfilePic +} + +fragment standardProfilePic on User { + profilePic(size: 50) +} +``` + +The queries `noFragments`, `withFragments`, and `withNestedFragments` all +produce the same response object. + +### Types on fragments + +Fragments must specify the type they apply to. In this example, `friendFields` +can be used in the context of querying a `User`. + +Fragments cannot be specified on any input value (scalar, enumeration, or input +object). + +Fragments can be specified on object types, interfaces, and unions. + +Selections within fragments only return values when concrete type of the object +it is operating on matches the type of the fragment. + +For example in this query on the Facebook data model: + +```graphql +query FragmentTyping +{ + profiles(handles: ["zuck", "cocacola"]) { + handle + ...userFragment + ...pageFragment + } +} + +fragment userFragment on User { + friends { count } +} + +fragment pageFragment on Page { + likers { count } +} +``` + +The `profiles` root field returns a list where each element could be a `Page` or a +`User`. When the object in the `profiles` result is a `User`, `friends` will be +present and `likers` will not. Conversely when the result is a `Page`, `likers` +will be present and `friends` will not. + +```js +{ + "profiles" : [ + { + "handle" : "zuck", + "friends" : { "count" : 1234 } + }, + { + "handle" : "cocacola", + "likers" : { "count" : 90234512 } + } + ] +} +``` + +### Query variables in fragments + +Query variables can be used within fragments. Query 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 +variable is referenced in a fragment and is included by an operation that does +not define that variable, the operation cannot be executed. + +### Inline fragments + +Fragments can be defined inline to query. This is done to conditionally execute +fields based on their runtime type. This feature of standard fragment inclusion +was demonstrated in the `query FragmentTyping` example. We could accomplish the +same thing using inline fragments. + +```graphql +query InlineFragmentTyping { + profiles(handles: ["zuck", "cocacola"]) { + handle + ... on User { + friends { count } + } + ... on Page { + likers { count } + } + } +} +``` + diff --git a/Section 3 -- Type System.md b/Section 3 -- Type System.md new file mode 100644 index 000000000..ab64c2fe8 --- /dev/null +++ b/Section 3 -- Type System.md @@ -0,0 +1,766 @@ +# Type System + +The GraphQL Type system describes the capabilities of a GraphQL server and is +used to determine if a query is valid. The type system also describes the +input types of query variables to determine if values provided at runtime +are valid. + +A GraphQL server's capabilities are referred to as that server's "schema". +A schema is defined in terms of the types and directives it supports. + +A given GraphQL schema must itself be internally valid. This section describes +the rules for this validation process where relevant. + +A GraphQL schema is represented by a root type for each kind of operation: +query and mutation; this determines the place in the type system where those +operations begin. + +All types within a GraphQL schema must have unique names. No two provided types +may have the same name. No provided type may have a name which conflicts with +any built in types (including Scalar and Introspection types). + +All directives within a GraphQL schema must have unique names. A directive +and a type may share the same name, since there is no ambiguity between them. + +## Types + +The fundamental unit of any GraphQL Schema is the type. There are eight kinds +of types in GraphQL. + +The most basic type is a `Scalar`. A scalar represents a primitive value, like +a string or an integer. Oftentimes, the possible responses for a scalar field +are enumerable. GraphQL offers an `Enum` type in those cases, where the type +specifies the space of valid responses. + +Scalars and Enums form the leaves in response trees; the intermediate levels are +`Object` types, which define a set of fields, where each field is another +type in the system, allowing the definition of arbitrary type hierarchies. + +GraphQL supports two abstract types: interfaces and unions. + +An `Interface` defines a list of fields; `Object` types that implement that +interface are guaranteed to implement those fields. Whenever the type system +claims it will return an interface, it will return a valid implementating type. + +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. + +All of the types so far are assumed to be both nullable and singular: e.g. a scalar +string returns either null or a singular string. The type system might want to +define that it returns a list of other types; the `List` type is provided for +this reason, and wraps another type. Similarly, the `Non-Null` type wraps +another type, and denotes that the result will never be null. These two types +are refered to as "wrapping types"; non-wrapping types are refered to as +"base types". A wrapping type has an underlying "base type", found by +continually unwrapping the type until a base type is found. + +Finally, oftentimes it is useful to provide complex structs as inputs to +GraphQL queries; the `Input Object` type allows the schema to define exactly +what data is expected from the client in these queries. + +### Scalars + +As expected by the name, a scalar represents a primitive value in GraphQL. +GraphQL responses take the form of a hierarchal tree; the leaves on these trees +are GraphQL scalars. + +All GraphQL scalars are representable as strings, though depending on the +response format being used, there may be a more appropriate primitive for the +given scalar type, and server should use those types when appropriate. + +GraphQL provides a number of built-in scalars, but type systems can add +additional scalars with semantic meaning. For example, a GraphQL system could +define a scalar called `Time` which, while serialized as a string, promises to +conform to ISO-8601. When querying a field of type `Time`, you can then rely on +the ability to parse the result with an ISO-8601 parser and use a +client-specific primitive for time. 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. + +**Result Coercion** + +A GraphQL server, when preparing a field of a given scalar type, must uphold the +contract the scalar type describes, either by coercing the value or +producing an error. + +For example, a GraphQL server could be preparing a field with the scalar type +`Int` and encounter a floating-point number. Since the server must not break the +contract by yielding a non-integer, the server should truncate the fractional +value and only yield the integer value. If the server encountered a boolean +`true` value, it should return `1`. If the server encountered a string, it may +attempt to parse the string for a base-10 integer value. If the server +encounters some value that cannot be reasonably coerced to an `Int`, then it +must raise an field error. + +Since this coercion behavior is not observable to clients of the GraphQL server, +the precise rules of coercion are left to the implementation. The only +requirement is that the server must yield values which adhere to the expected +Scalar type. + +**Input Coercion** + +If a GraphQL server expects a scalar type as input to a field argument, coercion +is observable and the rules must be well defined. If an input value does not +match a coercion rule, a query error must be raised. + +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 query 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 interpretted as an integer input value if +they have an empty fractional part (ex. `1.0`) and otherwise as floating-point +input value. + +#### Built-in Scalars + +GraphQL provides a basic set of well-defined Scalar types. A GraphQL server +should support all of these types, and a GraphQL server which provide a type by +these names must adhere to the behavior described below. + +##### Int + +The Int scalar type represents a signed 32-bit numeric non-fractional values. +Response formats that support a 32-bit integer or a number type should use +that type to represent this scalar. + +**Result Coercion** + +GraphQL servers should coerce non-int raw values to Int when possible +otherwise they must raise a field error. Examples of this may include returning +`1` for the floating-point number `1.0`, or `2` for the string `"2"`. + +**Input Coercion** + +When expected as an input type, only integer input values are accepted. All +other input values, including strings with numeric content, must raise a query +error indicating an incorrect type. If the integer input value represents a +value less than -231 or greater than or equal to 231, a +query 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. + +##### Float + +The Float scalar type represents signed double-precision fractional values +as specified by [IEEE 754](http://en.wikipedia.org/wiki/IEEE_floating_point). +Response formats that support an appropriate double-precision number type +should use that type to represent this scalar. + +**Result Coercion** + +GraphQL servers should coerce non-floating-point raw values to Float when +possible otherwise they must raise a field error. Examples of this may include +returning `1.0` for the integer number `1`, or `2.0` for the string `"2"`. + +**Input Coercion** + +When expected as an input type, both integer and float input values are +accepted. Integer input values are coerced to Float by adding an empty +fractional part, for example `1.0` for the integer input value `1`. All +other input values, including strings with numeric content, must raise a query +error indicating an incorrect type. If the integer input value represents a +value not representable by IEEE 754, a query error should be raised. + +##### String + +The String scalar type represents textual data, represented as UTF-8 character +sequences. The String type is most often used by GraphQL to represent free-form +human-readable text. All response formats must support string representations, +and that representation must be used here. + +**Result Coercion** + +GraphQL servers should coerce non-string raw values to String when possible +otherwise they must raise a field error. Examples of this may include returning +the string `"true"` for a boolean true value, or the string `"1"` for the +integer `1`. + +**Input Coercion** + +When expected as an input type, only valid UTF-8 string input values are +accepted. All other input values must raise a query error indicating an +incorrect type. + +##### Boolean + +The Boolean scalar type represents `true` or `false`. Response formats should +use a built-in boolean type if supported; otherwise, they should use their +representation of the integers `1` and `0`. + +**Result Coercion** + +GraphQL servers should coerce non-boolean raw values to Boolean when possible +otherwise they must raise a field error. Examples of this may include returning +`true` for any non-zero number. + +**Input Coercion** + +When expected as an input type, only boolean input values are accepted. All +other input values must raise a query error indicating an incorrect type. + +##### ID + +The ID scalar type represents a unique identifier, often used to refetch an +object or as 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** + +GraphQL is agnostic to ID format, and serializes to string to ensure consistency +across many formats ID could represent, from small auto-increment numbers, to +large 128-bit random numbers, to base64 encoded values, or string values of a +format like [GUID](http://en.wikipedia.org/wiki/Globally_unique_identifier). + +GraphQL servers should coerce as appropriate given the ID formats they expect, +when coercion is not possible they must raise a field error. + +**Input Coercion** + +When expected as an input type, any string (such as `"4"`) or integer (such +as `4`) input value should be coerced to ID as appropriate for the ID formats +a given GraphQL server expects. Any other input value, including float input +values (such as `4.0`), must raise a query error indicating an incorrect type. + + +### Objects + +GraphQL queries are hierarchal and composed, describing a tree of information. +While Scalar types describe the leaf values of these hierarchal queries, 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 are serialized as unordered maps, where the +queried field names (or aliases) are the keys and the result of evaluating +the field is the value. + +For example, a type `Person` could be described as: + +``` +type Person { + name: String + age: Int + picture: Url +} +``` + +Where `name` is a field that will yield a `String` value, and `age` is a field +that will yield an `Int` value, and `picture` 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 unordered map containing exactly the subset of the object +queried. Only fields that are declared on the object type may validly be queried +on that object. + +For example, selecting all the fields of `Person`: + +``` +{ + name, + age, + picture +} +``` + +Would yield the object: + +``` +{ + "name": "Mark Zuckerberg", + "age": 30, + "picture": "http://some.cdn/picture.jpg" +} +``` + +While selecting a subset of fields: + +``` +{ + name, + age +} +``` + +Must only yield exactly that subset: + +``` +{ + "name": "Mark Zuckerberg", + "age": 30 +} +``` + +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`: + +``` +type Person { + name: String + age: Int + picture: Url + relationship: Person +} +``` + +Valid queries must supply a nested field set for a field that returns +an object, so this query is not valid: + +```! +{ + name, + relationship +} +``` + +However, this example is valid: + +``` +{ + name, + relationship { + name + } +} +``` + +And will yield the subset of each object type queried: + +``` +{ + "name": "Mark Zuckerberg", + "relationship": { + "name": "Priscilla Chan" + } +} +``` + +**Result Coercion** + +Determining the result of coercing an object is the heart of the GraphQL +executor, so this is covered in that section of the spec. + +**Input Coercion** + +Objects are never valid inputs. + +#### Object Field Arguments + +Object fields are conceptually functions which yield values. Occasionally object +fields can accept arguments to further specify the return value. Field arguments +are defined as a list of all possible argument names and their expected input +types. + +For example, a `Person` type with a `picture` field could accept an argument to +determine what size of an image to return. + +``` +type Person { + name: String + picture(size: Int): Url +} +``` + +GraphQL queries can optionally specify arguments to their fields to provide +these arguments. + +This example query: + +``` +{ + name, + picture(size: 600) +} +``` + +May yield the result: + +``` +{ + "name": "Mark Zuckerberg", + "picture": "http://some.cdn/picture_600.jpg" +} +``` + +The type of a field argument can be a Scalar, as it was in this example, or an +Enum. It can also be an Input Object, covered later in this document, or it can +be any wrapping type whose underlying base type is one of those three. + +#### Object Field deprecation + +Fields in an object may be marked as deprecated as deemed necessary by the +application. It is still legal to query for these fields (to ensure existing +clients are not broken by the change), but the fields should be appropriately +treated in documentation and tooling. + +#### Object type validation + +Object types have the potential to be invalid if incorrectly defined. This set +of rules must be adhered to by every Object type in a GraphQL schema. + +1. The fields of an Object type must have unique names within that Object type; + no two fields may share the same name. +2. An object type must be a super-set of all interfaces it implements. + 1. The object type must include a field of the same name for every field + defined in an interface. + 1. The object field must include an argument of the same name for every + argument defined by the interface field. + 1. The object field argument must accept the same type (invariant) as + the interface field argument. + 2. The object field must be of a type which is equal to + the interface field. + + +### Interfaces + +GraphQL Interfaces represent a list of named fields and their arguments. GraphQL +object can then implement an interface, which guarantees that they will +contain the specified fields. + +Fields on a GraphQL interface have the same rules as fields on a GraphQL object; +their type can be Scalar, Object, Enum, Interface, or Union, or any wrapping +type whose base type is one of those five. + +For example, a interface may describe a required field and types such as +`Person` or `Business` may then implement this interface. + +``` +interface NamedEntity { + name: String +} + +type Person : NamedEntity { + name: String + age: Int +} + +type Business : NamedEntity { + name: String + employeeCount: Int +} +``` + +Fields which yield an interface are useful when one of many Object types are +expected, but some fields should be guaranteed. + +To continue the example, a `Contact` might refer to `NamedEntity`. + +``` +type Contact { + entity: NamedEntity + phoneNumber: String + address: String +} +``` + +This allows us to write a query for a `Contact` that can select the +common fields. + +``` +{ + entity { + name + }, + phoneNumber +} +``` + +When querying for 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 query: + +```! +{ + entity { + name, + age + }, + phoneNumber +} +``` + +because `entity` refers to a `NamedEntity`, and `age` is not defined on that +interface. Querying for `age` is only valid when the result of `entity` is a +`Person`; the query can express this using a fragment or an inline fragment: + +``` +{ + entity { + name, + ... on User { + age + } + }, + phoneNumber +} +``` + +**Result Coercion** + +The interface type should have some way of determining which object a given +result corresponds to. Once it has done so, the result coercion of the interface +is the same as the result coercion of the object. + +**Input Coercion** + +Interfaces are never valid inputs. + +#### Interface type validation + +Interface types have the potential to be invalid if incorrectly defined. + +1. The fields of an Interface type must have unique names within that Interface + type; no two fields may share the same name. + + +### Unions + +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 +typed fragments. + +For example, we might have the following type system: + +``` +Union SearchResult = Photo | Person + +type Person { + name: String + age: Int +} + +type Photo { + height: Int + width: Int +} + +type SearchQuery { + firstSearchResult: SearchResult +} +``` + +When querying the `firstSearchResult` field of type `SearchQuery`, the +query would ask for all fields inside of a fragment indicating the appropriate +type. If the query wanted the name if the result was a Person, and the height if +it was a photo, the following query is invalid, because the union itself +defines not fields: + +```! +{ + firstSearchResult { + name + height + } +} +``` + +Instead, the query would be: + +``` +{ + firstSearchResult { + ... on Person { + name + } + ... on Photo { + height + } + } +} +``` + +**Result Coercion** + +The union type should have some way of determining which object a given result +corresponds to. Once it has done so, the result coercion of the union is the +same as the result coercion of the object. + +**Input Coercion** + +Unions are never valid inputs. + +#### Union type validation + +Union types have the potential to be invalid if incorrectly defined. + +1. The member types of an Union type must all be Object base types; + Scalar, Interface and Union types may not be member types of a Union. + Similarly, wrapping types may not be member types of a Union. +2. A Union type must define two or more member types. + +When expected as an input type, array input values are accepted only when each +item in the input array can be accepted by the item type. If the input value is +not an array but can be accepted by the item type, it is coerced by creating an +array with the input value as the sole item. Other input values must raise a +query error indicating an incorrect type. + +### Enums + +GraphQL Enums are a variant on the Scalar type, which represents one of a +finite set of possible values. + +GraphQL Enums are not references for a numeric value, but are unique values in +their own right. They serialize as a string: the name of the represented value. + +**Result Coercion** + +GraphQL servers must return one of the defined set of possible values, if a +reasonable coercion is not possible they must raise a field error. + +**Input Coercion** + +GraphQL has a constant literal to represent enum input values. GraphQL string +literals must not be accepted as an enum input and instead raise a query error. + +Query 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 interpretted as the enum +input value with the same name. + + +### Input Objects + +Fields can define arguments that the client passes up with the query, +to configure their behavior. These inputs can be Strings or Enums, but +they sometimes need to be more complex than this. + +The `Object` type defined above is inappropriate for re-use here, because +`Object`s can contain fields that express circular references or references +to interfaces and unions, neither of which is appropriate for use as an +input argument. For this reason, input objects have a separate type in the +system. + +An `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. + +**Result Coercion** + +An input object is never a valid result. + +**Input Coercion** + +The input to an input object should be an unordered map, otherwise an error +should be thrown. The result of the coercion is an unordered map, with an +entry for each input field, whose key is the name of the input field. +The value of an entry in the coerced map is the result of input coercing the +value of the entry in the input with the same key; if the input does not have a +corresponding entry, the value is the result of coercing null. The input +coercion above should be performed according to the input coercion rules of the +type declared by the input field. + + +### Lists + +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 array is serialized as per +the item type. + +**Result Coercion** + +GraphQL servers 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 they 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. + +**Input Coercion** + +When accepted as an input, each item in the list should be coerced as per +the input coercion of the item type. + +If the value passed as an input to a list type is *not* as list, it should be +coerced as though the input was a list of size one, where the value passed is +the only item in the list. This is to allow inputs that accept a "var args" +to declare their input type as a list; if only one argument is passed (a common +case), the client can just pass that value rather than constructing the list. + +### 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 declares an underlying type, +and this type acts identically to that underlying type, with the exception +that `null` is not a valid response for the wrapping type. + +**Result Coercion** + +In all of the above result coercion, `null` was considered a valid value. +To coerce the result of a Non Null type, the result coercion of the +underlying 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 an error should be raised. + +**Input Coercion** + +When accepted as an input, each item in the list should be coerced as per +the input coercion of the item type. + +If the value passed as an input to a list type is *not* as list, it should be +coerced as though the input was a list of size one, where the value passed is +the only item in the list. This is to allow inputs that accept a "var args" +to declare their input type as a list; if only one argument is passed (a common +case), the client can just pass that value rather than constructing the list. + + +## Directives + +A GraphQL schema includes a list of supported directives, each of which has +a name. Directives can apply to operations, fragments, or fields; each directive +indicates which of those it applies to. + +Directives can optionally take an argument. The type of the argument to +a directive has the same restrictions as the type of an argument to a field. It +can be a Scalar, an Enum, an Input Object, or any wrapping type whose underlying +base type is one of those three. + +## Starting types + +A GraphQL schema includes types, indicating where query and mutation +operations start. This provides the initial entry points into the +type system. The query type must always be provided, and is an Object +base type. The mutation type is optional; if it is null, that means +the system does not support mutations. If it is provided, it must +be an object base type. + +The fields on the query type indicate what fields are available at +the top level of a GraphQL query. For example, a basic GraphQL query +like this one: + +```graphql +query getMe { + me +} +``` + +Is valid when the type provided for the query starting type has a field +named "me". Similarly + +```graphql +mutation setName { + setName(name: "Zuck") { + newName + } +} +``` + +Is valid when the type provided for the mutation starting type is not null, +and has a field named "setName" with a string argument named "name". diff --git a/Section 4 -- Introspection.md b/Section 4 -- Introspection.md new file mode 100644 index 000000000..23d6cc95e --- /dev/null +++ b/Section 4 -- Introspection.md @@ -0,0 +1,334 @@ +# Introspection + +A GraphQL server supports introspection over its schema. This schema is queried +using GraphQL itself, creating a powerful platform for tool-building. + +Take an example query for a trivial app. In this case there is a User type with +three fields: id, user, and birthday. + +For example, given a server with the following type definition: + +``` +type User { + id: String + name: String + birthday: Date +} +``` + +The query + +``` +{ + __type(name: "User") { + name + fields { + name + type { name } + } + } +} +``` + +would return + +``` +{ + "__type": { + "name" : "User", + "fields": [ + { "name": "id", "type": { "name": "String" } }, + { "name": "name", "type": { "name": "String" } }, + { "name": "birthday", "type": { "name": "Date" } }, + ] + } +} +``` + +## General Principles + +### Naming conventions + +Types and fields required by the GraphQL introspection system that are used in +the same context as user-defined type and fields are prefixed with two +underscores. This in order to avoid naming collisions with user-defined GraphQL +types. Conversely, GraphQL type system authors must not define any types, +fields, arguments, or any other type system artifact with two leading +underscores. + +### Documentation + +All types in the introspection system provide a `description` field of type +`String` to allow type designers to publish documentation in addition to +capabilities. A GraphQL server may return the `description` field using Markdown +syntax. Therefore it is recommended that any tool that displays description +use a Markdown renderer. + +### Deprecation + +To support the management of backwards compatibility, GraphQL fields and enum +values can indicate whether or not they are deprecated (`isDeprecated: Boolean`) +and a description of why it is deprecated (`deprecationReason: String`). + +Tools built using GraphQL introspection should respect deprecation by +discouraging deprecated use through information hiding or developer-facing +warnings. + +### Type Name Introspection + +GraphQL supports type name introspection within at any point in a query by the +meta field `__typename: String!` when querying against any Object, Interface, +or Union. It returns the name of the object type currently being queryied. + +This is most often used when querying against Interface or Union types to +identify which actual type of the possible types has been returned. + +This field is implicit and does not appear in the fields list in any defined type. + +## Schema Introspection + +The schema introspection system is accessible from the meta-fields `__schema` +and `__type` which are accessible from the type of the root of a query +operation. + +``` +__schema : __Schema! +__type(name: String) : __Type +``` + +These fields are implicit and do not appear in the fields list in the root type +of the query operation. + +The schema of the GraphQL schema introspection system: + +``` +type __Schema { + types: [__Type!]! + queryType: __Type! + mutationType: __Type + directives: [__Directive!]! +} + +type __Type { + kind: __TypeKind! + name: String + description: String + + # OBJECT and INTERFACE only + fields(includeDeprecated: Boolean = false): [__Field!] + + # OBJECT only + interfaces: [__Type!] + + # INTERFACE and UNION only + possibleTypes: [__Type!] + + # ENUM only + enumValues(includeDeprecated: Boolean = false): [__EnumValue!] + + # INPUT_OBJECT only + inputFields: [__InputValue!] + + # NON_NULL and LIST only + ofType: __Type +} + +type __Field { + name: String! + description: String + args: [__InputValue!]! + type: __Type! + isDeprecated: Boolean! + deprecationReason: String +} + +type __InputValue { + name: String! + description: String + type: __Type! + defaultValue: String +} + +type __EnumValue { + name: String! + description: String + isDeprecated: Boolean! + deprecationReason: String +} + +enum __TypeKind { + SCALAR + OBJECT + INTERFACE + UNION + ENUM + INPUT_OBJECT + LIST + NON_NULL +} + +type __Directive { + name: String! + description: String + type: __Type + onOperation: Boolean! + onFragment: Boolean! + onField: Boolean! +} + +``` + + +### The "__Type" Type + +`__Type` is at the core of the type introspection system. +It represents scalars, interfaces, object types, unions, enums in the system. + +`__Type` also represents type modifiers, which are used to modify a type +that it refers to (`ofType: __Type`). This is how we represent lists, +non-nullable types, and the combinations thereof. + +### Type Kinds + +There are several different kinds of type. In each kind, different fields are +actually valid. These kinds are listed in the `__TypeKind` enumeration. + +#### Scalar + +Represents scalar types such as Int, String, and Boolean. Scalars cannot have fields. + +A GraphQL type designer should describe the data format and scalar coercion +rules in the description field of any scalar. + +Fields + +* `kind` must return `__TypeKind.SCALAR`. +* `name` must return a String. +* `description` may return a String or {null}. +* All other fields must return {null}. + +#### Object + +Object types represent concrete instantiations of sets of fields. The +introspection types (e.g. `__Type`, `__Field`, etc) are examples of objects. + +Fields + +* `kind` must return `__TypeKind.OBJECT`. +* `name` must return a String. +* `description` may return a String or {null}. +* `fields`: The set of fields query-able on this type. + * Accepts the argument `includeDeprecated` which defaults to {false}. If + {true}, deprecated fields are also returned. +* `interfaces`: The set of interfaces that an object implements. +* All other fields must return {null}. + +#### Union + +Unions are an abstract types where no common fields are declared. The possible +types of a union are explicitly listed out in `possibleTypes`. Types can be +made parts of unions without modification of that type. + +Fields + +* `kind` must return `__TypeKind.UNION`. +* `name` must return a String. +* `description` may return a String or {null}. +* `possibleTypes` returns the list of types that can be represented within this + union. They must be object types. +* All other fields must return {null}. + +#### Interface + +Interfaces is an abstract type where there are common fields declared. Any type +that implements an interface must define all the fields with names and types +exactly matching. The implementations of this interface are explicitly listed +out in `possibleTypes`. + +Fields + +* `kind` must return `__TypeKind.INTERFACE`. +* `name` must return a String. +* `description` may return a String or {null}. +* `fields`: The set of fields required by this interface. + * Accepts the argument `includeDeprecated` which defaults to {false}. If + {true}, deprecated fields are also returned. +* `possibleTypes` returns the list of types that can be represented within this + union. They must be object types. +* All other fields must return {null}. + +#### Enum + +Enums are special scalars that can only have a defined set of values. + +Fields + +* `kind` must return `__TypeKind.ENUM`. +* `name` must return a String. +* `description` may return a String or {null}. +* `enumValues`: The list of `EnumValue`. There must be at least one and they + must have unique names. + * Accepts the argument `includeDeprecated` which defaults to {false}. If + {true}, deprecated enum values are also returned. +* All other fields must return {null}. + +#### Input Object + +Input objects are composite types used as inputs into queries defined as a list +of named input values. + +For example the an input object `Point` could be defined as: + +``` +type Point { + x: Int + y: Int +} +``` + +Fields + +* `kind` must return `__TypeKind.INPUT_OBJECT`. +* `name` must return a String. +* `description` may return a String or {null}. +* `inputFields`: a list of `InputValue`. +* All other fields must return {null}. + +#### List + +Lists represent sequences of values in GraphQL. A List type is a type modifier: +it wraps another type instance in the `ofType` field, which defines the type of +each item in the list. + +Fields + +* `kind` must return `__TypeKind.LIST`. +* `ofType`: Any type. +* All other fields must return {null}. + +#### Non-null + +GraphQL types are nullable. The value {null} is a valid response for field type. + +A Non-null type is a type modifier: it wraps another type instance in the +`ofType` field. Non-null types do not allow {null} as a response, and indicate +required inputs for arguments and input object fields. + +* `kind` must return `__TypeKind.NON_NULL`. +* `ofType`: Any type except Non-null. +* All other fields must return {null}. + +#### Combining List and Non-Null + +List and Non-Null can compose, representing more complex types. + +If the modified type of a List is Non-Null, then that List may not contain any +{null} items. + +If the modified type of a Non-Null is List, then {null} is not accepted, +however an empty list is accepted. + +If the modified type of a List is a List, then each item in the first List is +another List of the second List's type. + +A Non-Null type cannot modify another Non-Null type. diff --git a/Section 5 -- Validation.md b/Section 5 -- Validation.md new file mode 100644 index 000000000..18f0084de --- /dev/null +++ b/Section 5 -- Validation.md @@ -0,0 +1,1197 @@ +# Validation + +GraphQL does not just verify if a request is syntatically correct. + +Prior to execution, it can also verify that a request is valid +within the context of a given GraphQL schema. Validation is primarily +targeted at development-time tooling. Any client-side tooling +should returns errors and not allow the formulation of queries +known to violate the type system at a given point in time. + +Total request validation on the server-side during execution is optional. As +schemas and systems change over time existing clients may end up emitting +queries that are no longer valid given the current type system. Servers +(as described in the Execution section of this spec) attempt to satisfy as +much as the request as possible and continue to execute in the presence +of type system errors rather than cease execution completely. + +For this section of this schema, we will assume the following type system +in order to demonstrate examples: + +```graphql + +enum DogCommand { SIT, DOWN, HEEL } + +type Dog : Pet { + name: String!, + nickname: String, + barkVolume : Int, + doesKnowCommand(dogCommand: DogCommand!) : Boolean! + isHousetrained(atOtherHomes: Boolean): Boolean! +} + +interface Sentient { name: String! } +interface Pet { name: String!, nickname: String } + +type Alien : Sentient { name: String!, homePlanet: String } +type Human : Sentient { name: String! } + +type Cat : Pet { + name: String!, + nickname: String, + meowVolume: Int +} + +union CatOrDog = Cat | Dog +union DogOrHuman = Dog | Human +union HumanOrAlien = Human | Alien +``` + +## Fields + +### Field Selections on Objects, Interfaces, and Unions Types + +** Formal Specification ** + + * For each {selection} in the document. + * Let {fieldName} be the target field of {selection} + * {fieldName} must be defined on type in scope + +** Explanatory Text ** + +The target field of a field selection must defined on the scoped type of the +selection set. There are no limitations on alias names. + +For example the following fragment would not pass validation: + +```!graphql + +fragment fieldNotDefined on Dog { + meowVolume +} + +fragment aliasedLyingFieldTargetNotDefined on Dog { + barkVolume : kawVolume +} +``` + +For interfaces, direct field selection can only be done on fields. Fields +of concrete implementors is not relevant to the validity of the given +interface-typed selection set. + +For example, the following is valid: + +```graphql +fragment interfaceFieldSelection on Pet { + name +} +``` + +and the following is invalid: + +```!graphql +fragment definedOnImplementorsButNotInterface : Pet { + nickname +} +``` + +Because fields are not declared on unions, direct field selection on +union-typed selection set. This is true even if concrete +implementors of the union define the fieldName. + +For example the following is invalid + +```graphql +fragment directFieldSelectionOnUnion : CatOrDog { + directField +} + +fragment definedOnImplementorsQueriedOnUnion : CatOrDog { + name +} +``` + +### Field Selection Merging + +** Formal Specification ** + + * Let {set} be any selection set defined in the GraphQL document + * Let {setForKey} be the set of selections with a given response key in {set} + * All members of {setForKey} must: + * Have identical target fields + * Have identical sets of arguments name-value pairs. + * Have identical sets of directive name-value pairs. + +** Explanatory Text ** + +Selection names are de-duplicated and merged for validation, but the target +field, arguments, and directives must all be identical. + +For human-curated GraphQL, this rules seem a bit counterintuitive since it +appears to be clear developer error. However in the presence of nested +fragments or machine-generated GraphQL, requiring unique selections is a +burdensome limitation on tool authors. + +The following selections correctly merge: + +```graphql +fragment mergeIdenticalFields on Dog { + name + name +} + +fragment mergeIdenticalAliasesAndFields on Dog { + otherName : name + otherName : name +} +``` + +The following is not able to merge: + +```!graphql + +fragment conflictingBecauseAlias on Dog { + name : nickname + name +} + +``` + +Identical field arguments are also merged if they have +identical arguments. Both values and variables can be +correctly merged. + +For example the following correctly merge: + +```graphql + +fragment mergeIdenticalFieldsWithIdenticalArgs on Dog { + doesKnowCommand(dogCommand: SIT) + doesKnowCommand(dogCommand: SIT) +} + +fragment mergeIdenticalFieldsWithIdenticalValues on Dog { + doesKnowCommand(dogCommand: $dogCommand) + doesKnowCommand(dogCommand: $dogCommand) +} + +``` + +The following do not correctly merge: + +```!graphql + +fragment conflictingArgsOnValues on Dog { + doesKnowCommand(dogCommand: SIT) + doesKnowCommand(dogCommand: HEEL) +} + +fragment conflictingArgsValueAndVar on Dog { + doesKnowCommand(dogCommand: SIT) + doesKnowCommand(dogCommand: $dogCommand) +} + +fragment conflictingArgsWithVars on Dog { + doesKnowCommand(dogCommand: $varOne) + doesKnowCommand(dogCommand: $varTwo) +} +``` + +The same logic applies to directives. The set of directives on each selection +with the same response key in a given scope must be identical. + +The following is valid: + +```graphql +fragment mergeSameFieldsWithSameDirectives on Dog { + name @if:true + name @if:true +} +``` + +and the following is invalid: + +```!graphql + +fragment conflictingDirectiveArgs on Dog { + name @if: true + name @unless: false +} + +``` + +### Leaf Field Selections + +** Formal Specification ** + + * For each {selection} in the document + * Let {selectionType} be the result type of {selection} + * If {selectionType} is a scalar: + * 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 are never allowed: scalars +are the leaf nodes of any GraphQL query. + +The following is valid. + +```graphql +fragment scalarSelection: Dog { + barkVolume +} +``` + +The following is invalid. + +```!graphql +fragment scalarSelectionsNotAllowedOnBoolean : Dog { + barkVolume { sinceWhen } +} +``` + +Conversely the leaf field selections of GraphQL queries +must be scalars. Leaf selections on objects, interfaces, +and unions without subfields are disallowed. + +Let's assume the following query root type of the schema: + +```graphql +type QueryRoot { + human: Human + pet: Pet + catOrDog: CatOrDog +} +``` + +The following examples are invalid + +```!graphql + +query directQueryOnObjectWithoutSubFields +{ + human +} + +query directQueryOnInterfaceWithoutSubFields +{ + pet +} + +query directQueryOnUnionWithoutSubFields +{ + catOrDog +} +``` + +## Arguments + +### Argument Names + +** Formal Specification ** + + * For each {selection} in the document + * Let {arguments} be the set of argument provided to the {selection} + * Let {targetField} be the target field of a given {selection} + * Let {argumentDefinitions} be the set of argument definitions of {targetField} + * Each {argumentName} in {arguments} must have a corresponding argument defintion + in the {targetField} with the same name + +** Explanatory Text ** + +Field selections may take arguments. Each field selection corresponds to a +field definition on the enclosing type, which specifies a set of possible +arguments. Every argument provided to the selection must be defined in the set +of possible arguments. + +For example the following are valid: + +```graphql +fragment argOnRequiredArg on Dog { + doesKnowCommand(dogCommand: SIT) +} + +fragment argOnOptional on Dog { + isHousetrained(atOtherHomes: true) +} +``` + +and the following is invalid since command is not defined on DogCommand: + +```!graphql + +fragment invalidArgName on Dog { + doesKnowCommand(command: CLEAN_UP_HOUSE) +} + +``` + +In order to explore more complicated argument examples, let's add the following +to our type system: + +```graphql +type Arguments { + multipleReqs(x: Int!, y: Int!) + booleanArgField(booleanArg: Boolean) + floatArgField(floatArg: Float) + intArgField(intArg: Int) + nonNullBooleanArgField(nonNullBooleanArg: Boolean!) +} +``` + +Order does not matter in arguments. Therefore both the following example are valid. + +```graphql +fragment multipleArgs on Arguments { + multipleReqs(x: 1, y: 2) +} + +fragment multipleArgsReverseOrder on Arguments { + multipleReqs(y: 1, x: 2) +} +``` + +### Argument Values Type Correctness + +#### Compatible Values + +** Formal Specification ** + + * For each {selection} in the document + * Let {arguments} be the set of argument provided to the {selection} + * Let {targetField} be the target field of a given {selection} + * Let {argumentDefinitions} be the set of argument definitions of {targetField} + * For each {literalArgument} of all {arguements} with literal for values. + * The type of {literalArgument} must equal the type of the argument defintion OR + * The type of {literalArgument} must be coercable to type of the argument definition + +** Explanatory Text ** + +Argument literal values must be compatible with the type defined on the type that +literal is being passed to. + +This means either + * the types must match equally or + * the types must be coercable. + +For example, an Int can be coerced into a Float. + +```graphql +fragment goodBooleanArg on Arguments { + booleanArgField(booleanArg: true) +} + +fragment coercedIntIntoFloatArg on Arguments { + floatArgField(floatArg: 1) +} +``` + +An uncoerceable conversion, is string to int. Therefore, the +following example is invalid. + +```!graphql +fragment stringIntoInt on Arguments { + intArgField(intArg: "3") +} +``` + +#### Argument Optionality + + * For each {selection} in the document + * Let {arguments} be the set of argument provided to the {selection} + * Let {targetField} be the target field of a given {selection} + * Let {argumentDefinitions} be the set of argument definitions of {targetField} + * For each {definition} in {argumentDefinition}, if the type of {definition} is non-null + a value must be provided. + +** Explanatory Text ** + +Field arguments can be required. Field arguments are required if the type of the argument +is non-null. If it is not non-null, the argument is optional. Optional arguments +must have default values. + +For example the following are valid: + +```graphql +fragment goodBooleanArg on Arguments { + booleanArgField(booleanArg: true) +} + +fragment goodNonNullArg on Arguments { + nonNullBooleanArgField(nonNullBooleanArg: true) +} + +``` + +On a field with a a nullable arg, that argument can be omitted. + +Therefore the following query is valid: + +```graphql + +fragment goodBooleanArgDefault on Arguments { + booleanArgField +} +``` + +but this is not valid on a non-null argument. + +```!graphql +fragment missingRequiredArg on Arguments { + notNullBooleanArgField +} +``` + +## Fragments + +### Fragment Declarations + +#### 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 + +** 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 query does not validate. + +For example the following fragments are valid: + +```graphql + +fragment CorrectType on Dog { + name +} + +fragment InlineFragment on Dog { + ... on Dog { + name + } +} + +``` + +and the following do not validate: + +```!graphql + +fragment NotOnExistingType on NotInSchema { + name +} + +fragment InlineNotExistingType on Dog { + ... on NotInSchema { name } +} +``` + +#### Fragments On Composite Types + +** Formal Specification ** + + * For each {fragment} defined in the document. + * The target type of fragment must be have kind {UNION}, {INTERFACE}, or + {OBJECT}. + +** Explanatory Text ** + +Fragments can only be declared on unions, interfaces, and objects. They are +invalid on scalars. They can only be applied on non-leaf fields. This rule +applies to both inline and named fragments. + +The following fragment declarations are valid: + +```graphql +fragment fragOnObject on Dog { name } +fragment fragOnInterface on Pet { name } +fragment fragOnUnion on CatOrDog { ... on Dog { name } } +``` + +and the following are invalid: + +```!graphql +fragment fragOnScalar on Int { something } +fragment inlineFragOnScalar on Dog { ... on Boolean { somethingElse } } +``` + +#### Fragments Must Be Used + +** Formal Specification ** + + * For each {fragment} defined in the document. + * {fragment} must be be the target of at least one spread in the document + +** Explanatory Text ** + +Defined fragments must be used within a query document. + +For example the following is an invalid query document: + +```!graphql +fragment nameFragment on Dog { // unused + name +} +{ dog { name } } +``` + +### Fragment Spreads + +Field selection is also determined by spreading fragments into one +another. The selection set of the target fragment is unioned with +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 + +** Explanatory Text ** + +Named fragment spreads must refer to fragments defined +within the document. If the target of a spread is +not defined, this is an error: + +```!graphql +fragment nameFragment on Dog { name } +{ dog { ...undefinedFragment} } +``` + +#### Fragment spreads must not form cycles + +** Formal Specification ** + + * For each {fragmentDefinition} in the document + * Let {visited} be the empty set. + * {DetectCycles(fragmentDefinition, visited)} + +{DetectCycles(fragmentDefinition, visited)} : + * Let {spreads} be all fragment spread descendents 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} + * {DetectCycles(nextFragmentDefinition, nextVisited)} + +** Explanatory Text ** + +The graph of fragment spreads must not form any cycles including spreading itself. +Otherwise an operation could infinitely spread or infinitely execute on cycles +in the underlying data. + +This invalidates fragments that would result in an infinite spread: + +```!graphql +{ + dog { + ...nameFragment + } +} + +fragment nameFragment on Dog { + name + ...barkVolumeFragment +} + +fragment barkVolumeFragment on Dog { + barkVolume + ...nameFragment +} +``` + +If the above fragments were inlined, this would result in the infinitely large: + +```!graphql +{ + dog { + name, barkVolume, name, barkVolume, name, barkVolume, name, # etc... + } +} +``` + +This also invalidates fragments that would result in an infinite recursion when +executed against cyclic data: + +```!graphql +{ + dog { + ...dogFragment + } +} + +fragment dogFragment on Dog { + name + owner { + ...ownerFragment + } +} + +fragment ownerFragment on Dog { + name + pets { + ...dogFragment + } +} +``` + +#### Fragment spread is possible + +** Formal Specification ** + + * For each {spread} (named or inline) in 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} + +** 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. + +and the following valid fragments: + +##### Object Spreads In Object Scope + +In the scope of a object type, the only valid object type +fragment spread is one that applies to the same type that +is in scope. + +For example + +```graphql +fragment dogFragment on Dog { ... on Dog { barkVolume } } +``` + +and the following is invalid + +```!graphql +fragment catInDogFragmentInvalid on Dog { ... on Cat { meowVolume } } +``` + +##### 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. + +For example + +```graphql +fragment petNameFragment on Pet { name } +fragment interfaceWithinObjectFragment on Dog { ...petNameFragment } +``` + +is valid because {Dog} implements Pet. + +Likewise + +```graphql +fragment CatOrDogNameFragment on CatOrDog { ... on Cat { meowVolume } } +fragment unionWithObjectFragment on Dog { ...CatOrDogFragment } +``` + +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 the 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 the +that interface or union. + +For example, the following fragments are valid: + +```graphql +fragment petFragment on Pet { name, ... on Dog { barkVolume } } +fragment catOrDogFragment on CatOrDog { ... on Cat { meowVolume } } +``` + +{petFragment} is valid because {Dog} implements the interface {Pet}. +{catOrDogFragment} is valid because {Cat} is a member of the +{CatOrDog} union. + +By contrast the following fragments are invalid: + +```!graphql +fragment sentientFragment on Sentient { ... on Dog { barkVolume } } +fragment humanOrAlienFragment on HumanOrAlien { ... on Cat { meowVolume } } +``` + +{Dog} does not implement the interface {Sentient} and therefore +{sentientFragment} can never return meaningful results. Therefore the fragment +is invalid. Likewise {Cat} is not a member of the union {HumanOrAlien}, and it +can also never return meaningful results, making it invalid. + +##### Abstract Spreads in Abstract Scope + +Union or interfaces fragments can be used within eachother. As long as there +exists at least *one* object type that exists in the intersection of the +possible types of the scope and the spread, the spread is considered valid. + +So for example + +```graphql +fragment unionWithInterface Pet { ...dogOrHumanFragment } +fragment dogOrHumanFragment on DogOrHuman { ... on Dog { barkVolume } } +``` + +is consider valid because {Dog} implements interface {Pet} and is a +member of {DogOrHuman}. + +However + +```!graphql +fragment nonIntersectingInterfaces on Pet { ...sentientFragment } +fragment sentientFragment on Sentient { name } +``` + +is not valid because there exists no type that implements both {Pet} +and {Sentient}. + +## Directives + +### Directives Are Defined + +** Formal Specification ** + + * For every {directiveUse} in a document. + * The name of that directive must be defined by the type system of + the GraphQL server. + +** Explanatory Text ** + +GraphQL servers define what directives they support. For each +usage of a directive, the directive must be available on that server. + +### Directive Arguments Are Of Correct Type + +** Formal Specification ** + + * For every {directiveUse} in a document. + * Let {directiveType} be the input type of the corresponding + directive defined on the server. + * If {directiveType} is not defined: + * The directive is meant to be used only as flag, and no argument should be + provided. + * If {directiveType} is defined and non-null: + * {directiveUse} must have an argument + * Let {argumentType} be the type of argument supplied to {directiveUse} + * {argumentType} and {directiveType} must be the same or {argumentType} must + be coercable to {directiveType} + +** Explanatory Text ** + +Directive arguments follow similar rules to arguments on fields. Much like +field arguments, arguments to directives must be of the same type or +coercable to input type of the directive type. + +Directives arguments differ from field arguments insofar as they can +be used without a provided argument. If the type of directive is not non-null, +the directive can be optionally used without an argument. If the type of +a directive is not defined, it is a flag directive: it cannot have an argument, +If a value is provided to a flag directive, this is a validation error. + +## Operations + +### Variables + +#### Variable Default Values Are Correctly Typed + +** Formal Specification ** + + * For every {operation} in a document + * For every {variable} on each {operation} + * Let {variableType} be the type of {variable} + * If {variableType} is non-null it cannot have a default value + * If {variable} has a default value it must be of the same types + or able to be coerced to {variableType} + +** Explanatory Text ** + +Variable defined by operations are allowed to define default values +if the type of that variable not non-null. + +For example the following query will pass validation. + +```graphql + query HouseTrainedQuery($atOtherHomes: Boolean = true) { + dog { isHousetrained(atOtherHomes: $atOtherHomes) } + } +``` + +However if the variable is defined as non-null, default values +are unreachable. Therefore queries such as the following fail +validation + +```!graphql + query HouseTrainedQuery($atOtherHomes: Boolean! = true) { + dog { isHousetrained(atOtherHomes: $atOtherHomes) } + } +``` + +Default values must be compatible with the types of variables. +Types much match or they must be coercable to the type. + +Non-matching types fail, such as in the following example: + +```!graphql + query HouseTrainedQuery($atOtherHomes: Boolean = "true") { + dog { isHousetrained(atOtherHomes: $atOtherHomes) } + } +``` + +However if a type is coerceable the query will pass validation. + +For example: + +```graphql + query IntToFloatQuery($floatVar: Float = 1) { + arguments { + floatArgField(floatArg: $floatVar) + } + } +``` + +#### 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} + * {variableType} must of kind {SCALAR}, {ENUM} or {INPUT_OBJECT} + +** Explanatory Text ** + +Variables can only be scalars, enums, input objects, or lists and non-null +variants of those types. These are known as input types. Object, unions, +and interfaces cannot be used as inputs. + +The following queries are valid: + +```graphql + query TakesBoolean($atOtherHomes: Boolean) { /* ... */ } + query TakesComplexInput($complexInput: ComplexInput) { /* ... */ } + query TakesListOfBooleanBang($booleans: [Boolean!]) { /* ... */ } +``` + +The following queries are invalid: + +```!graphql + query TakesCat($cat: Cat) { /* ... */ } + query TakesDogBang($dog: Dog!) { /* ... */ } + query TakesListOfPet($pets: [Pet]) { /* ... */ } + query TakesCatOrDog($catOrDog: CatOrDog) { /* ... */ } +``` + +#### All Variable Uses Defined + +** Formal Specification ** + + * For each {operation} in a document + * For each {variableUsage} in scope, variable must be operation's variable list. + * Let {fragments} be every fragment reference by that operation transitively + * For each {fragment} in {fragments} + * For each {variableUsage} in scope of {fragment}, variable must be + {operation}'s variable list. + +** Explanatory Text ** + +Variables are scoped on a per-operation basis. That means that any variable +used within the context of a operation must be defined at the top level of that +operation + +For example: + +```graphql +query VariableIsDefined($atOtherHomes: Boolean) { + dog { isHousetrained(atOtherHomes: $booleanArg) +} +``` + +is valid. ${atOtherHomes} is defined by the operation. + +By constract the following query is invalid: + +```!graphql +query VariableIsNotDefined { + dog { isHousetrained(atOtherHomes: $atOtherHomes) +} +``` + +${atOtherHomes} is not defined by the operation. + +Fragments complicate this rule. Any fragment transitively included by an +operation has access to the variables defined by that operation. Fragments +can appear within multiple operations and therefore variable usages +must correspond to variable definitions in all of those operations. + +For example the following is valid: + +```graphql +query VariableIsDefinedUsedInSingleFragment($atOtherHomes: Boolean) { + dog { ...isHousetrainedFragment } +} + +fragment isHousetrainedFragment on Dog { + isHousetrained(atOtherHomes: $atOtherHomes} +} +``` + +since {isHousetrainedFragment} is used within the context of the operation +{VariableIsDefinedUsedInSingleFragment} and the variable is defined by that +operation. + +On the contrary is a fragment is included within an operation that does +not define a referenced variable, this is a validation error. + +```!graphql +query VariableIsNotDefinedUsedInSingleFragment { + dog { ...isHousetrainedFragment } +} + +fragment isHousetrainedFragment on Dog { + isHousetrained(atOtherHomes: $atOtherHomes} +} +``` + +This applies transitively as well, so the following also fails: + +```!graphql +query VariableIsNotDefinedUsedInNestedFragment { + dog { ...outerHousetrainedFragment } +} + +fragment outerHousetrainedFragment on Dog { + ...isHousetrainedFragment +} + +fragment isHousetrainedFragment on Dog { + isHousetrained(atOtherHomes: $atOtherHomes} +} +``` + +Variables must be defined in all operations in which a fragment +is used. + +```graphql +query HousetrainedQueryOne($atOtherHomes: Boolean) { + dog { ...isHousetrainedFragment } +} + +query HousetrainedQueryTwo($atOtherHomes: Boolean) { + dog { ...isHousetrainedFragment } +} + +fragment isHousetrainedFragment on Dog { + isHousetrained(atOtherHomes: $atOtherHomes} +} +``` + +However the following does not validate: + +```!graphql + +query HousetrainedQueryOne($atOtherHomes: Boolean) { + dog { ...isHousetrainedFragment } +} + +query HousetrainedQueryTwoNotDefined { + dog { ...isHousetrainedFragment } +} + +fragment isHousetrainedFragment on Dog { + isHousetrained(atOtherHomes: $atOtherHomes} +} +``` + +This is because {HousetrainedQueryTwoNotDefined} does not define +a variable ${atOtherHomes} but that variable is used by {isHousetrainedFragment} +which is included in that operation. + +#### 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 + operation. + +** Explanatory Text ** + +All variables defined by an operation must be used in that operation or a +fragment transitively included by that operation. Unused variables cause +a validation error. + +For example the following is invalid: + +```!graphql +query VariableUnused($atOtherHomes: Boolean) { + dog { isHousetrained } +} +``` + +because ${atOtherHomes} in not referenced. + +These rules apply to transitive fragment spreads as well: + +```graphql +query VariableUsedInFragment($atOtherHomes: Boolean) { + dog { ...isHousetrainedFragment } +} + +fragment isHousetrainedFragment on Dog { + isHousetrained(atOtherHomes: $atOtherHomes) +} +``` + +The above is valid since ${atOtherHomes} is used in {isHousetrainedFragment} +which is included by {VariableUsedInFragment}. + +If that fragment did not have a reference to ${atOtherHomes} it would be not valid: + +```!graphql +query VariableNotUsedWithinFragment($atOtherHomes: Boolean) { + ...isHousetrainedWithoutVariableFragment +} + +fragment isHousetrainedWithoutVariableFragment on Dog { + isHousetrained +} +``` + +All operations in a document must use all of their variables. + +As a result, the following document does not validate. + +```!graphql + +query QueryWithUsedVar($atOtherHomes: Boolean) { + dog { ...isHousetrainedFragment } +} + +query QueryWithExtraVar($atOtherHomes: Boolean, $extra: Int) { + dog { ...isHousetrainedFragment } +} + +fragment isHousetrainedFragment on Dog { + isHousetrained(atOtherHomes: $atOtherHomes) +} +``` + +This document is not valid because {QueryWithExtraVar} defines +an extraneous variable. + +#### 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 {variableType} be the type of variable definition in the operation + * Let {argumentType} be the type of the argument the variable is passed to. + * Let {hasDefault} be true if the variable definition defines a default. + * AreTypesCompatible({argumentType}, {variableType}, {hasDefault}) must be true + + * AreTypesCompatible({argumentType}, {variableType}, {hasDefault}): + * If {hasDefault} is true, treat the {variableType} as non-null. + * If inner type of {argumentType} and {variableType} be different, return false + * If {argumentType} and {variableType} have different list dimensions, return false + * If any list level of {variableType} is not non-null, and the corresponding level + in {argument} is non-null, the types are not compatible. + +** 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 not-null argument type. + +Types must match: + +```!graphql +Query IntCannotGoIntoBoolean($intArg: Int) { + arguments { booleanArgField(booleanArg: $intArg) } +} +``` + +${intArg} typed as {Int} cannot be used as a argument to {booleanArg}, typed as {Boolean}. + +List cardinality must also be the same. For example, lists cannot be passed into singular +values. + +```!graphql +Query BooleanListCannotGoIntoBoolean($booleanListArg: [Boolean]) { + arguments { booleanArgField(booleanArg: $booleanListArg) } +} +``` + +Nullability must also be respected. In general a nullable variable cannot +be passed to a non-null argument. + +```!graphql +Query BooleanArgQuery($booleanArg: Boolean) { + arguments { nonNullBooleanArgField(nonNullBooleanArg: $booleanArg) } +} +``` + +A notable exception is when default arguments are provided. They are, in effect, +treated as non-nulls. + +```graphql +Query BooleanArgQueryWithDefault($booleanArg: Boolean = true) { + arguments { nonNullBooleanArgField(nonNullBooleanArg: $booleanArg) } +} +``` + +For list types, the same rules around nullability apply to both outer types +and inner types. A nullable list cannot be passed to a non-null list, and a lists +of nullable values cannot be passed to a list of non-null values. + +```graphql + +Query NonNullListToList($nonNullBooleanList: ![Boolean]) { + arguments { booleanListArgField(booleanListArg: $nonNullBooleanList) } +} + +``` + +However a nullable list could not be passed to a non-null list. + +```!graphql + +Query ListToNonNullList($booleanList: [Boolean]) { + arguments { nonNullBooleanListField(nonNullBooleanListArg: $booleanList) } +} + +``` + +This would fail validation because a `[T]` cannot be passed to a `[T]!`. + +Similarily a `[T]` cannot be passed to a `[T!]`. diff --git a/Section 6 -- Execution.md b/Section 6 -- Execution.md new file mode 100644 index 000000000..c30c1c4e6 --- /dev/null +++ b/Section 6 -- Execution.md @@ -0,0 +1,296 @@ +# Execution + +This section describes how GraphQL generates a response from a request + +## Evaluating requests + +To evaluate a request, the executor will have a parsed `Document` (as defined +in the “Query Language” part of this spec) and a selected operation name to +run. + +The executor should find the `Operation` in the `Document` with the given +operation name. If no such operation exists, the executor should throw an +error. If the operation is found, then the result of evaluating the request +should be the result of evaluating the operation according to the “Evaluating +operations” section. + +## Evaluating operations + +The type system, as described in the “Type System” part of the spec, must +provide a “Query Root” and a “Mutation Root” object. + +If the operation is a mutation, the result of the operation is the result of +evaluating the mutation’s top level selection set on the “Mutation Root” +object. This selection set should be evaluated serially. + +If the operation is a query, the result of the operation is the result of +evaluating the query’s top level selection set on the “Query Root” object. + +## Evaluating selection sets + +To evaluate a selection set, the executor needs to know the object on which it +is evaluating the set and whether it is being evaluated serially. + +If the selection set is being evaluated on the `null` object, then the result +of evaluating the selection set is `null`. + +Otherwise, the selection set is turned into a grouped field set; each entry in +the grouped field set is a list of fields that share a responseKey. + +The selection set is converted to a grouped field set by calling +`CollectFields`, initializing `visitedFragments` to an empty list. + +CollectFields(objectType, selectionSet, visitedFragments): + + * Initialize {groupedFields} to an empty list of lists. + * For each {selection} in {selectionSet}; + * If {selection} is a Field: + * Let {responseKey} be the response key of {selection}. + * 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 {fragmentGroupedFields} be the result of calling + {CollectFields(objectType, fragmentSelectionSet)}. + * For each {fragmentGroup} in {fragmentGroupedFields}: + * 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 inline fragment: + * Let {fragmentType} be the type condition on {selection}. + * If {doesFragmentTypeApply(objectType, fragmentType)} is false, continue + with the next {selection} in {selectionSet}. + * Let {fragmentSelectionSet} be the top-level selection set of {selection}. + * Let {fragmentGroupedFields} be the result of calling {CollectFields(objectType, fragmentSelectionSet)}. + * For each {fragmentGroup} in {fragmentGroupedFields}: + * 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}. + +doesFragmentTypeApply(objectType, fragmentType): + + * If {fragmentType} is an Object Type, return {true} if {objectType} is + {fragmentType}, otherwise return {false}. + * If {fragmentType} is an Interface Type, return {true} if {objectType} is an + implementation of {fragmentType}, otherwise return {false}. + * If {fragmentType} is a Union, return {true} if {objectType} is a possible + type of {fragmentType}, otherwise return {false}. + +The result of evaluating the selection set is the result of evaluating the +corresponding grouped field set. The corresponding grouped field set should be +evaluated serially if the selection set is being evaluated serially, otherwise +it should be evaluated normally. + +## Evaluating a grouped field set + +The result of evaluating a grouped field set will be an unordered map. There +will be an entry in this map for every item in the grouped field set. + +### Field entries + +Each item in the grouped field set can potentially create an entry in the +result map. That entry in the result map is the result is the result of calling +`GetFieldEntry` on the corresponding item in the grouped field set. +`GetFieldEntry` can return `null`, which indicates that there should be no +entry in the result map for this item. Note that this is distinct from +returning an entry with a string key and a null value, which indicates that an +entry in the result should be added for that key, and its value should be null. + +`GetFieldEntry` assumes the existence of two functions that are not defined in +this section of the spec. It is expected that the type system provides these +methods: + + * `ResolveFieldOnObject`, which takes an object type, a field, and an object, + and returns the result of resolving that field on the object. + * `GetFieldTypeFromObjectType`, which takes an object type and a field, and + returns that field's type on the object type, or `null` if the field is not + valid on the object type. + +GetFieldEntry(objectType, object, fields): + + * Let {firstField} be the first entry in the ordered list {fields}. Note that + {fields} is never empty, as the entry in the grouped field set would not + exist if there were no fields. + * Let {responseKey} be the response key of {firstField}. + * Let {fieldType} be the result of calling + {GetFieldTypeFromObjectType(objectType, firstField)}. + * If {fieldType} is {null}, return {null}, indicating that no entry exists in + the result map. + * Let {resolvedObject} be {ResolveFieldOnObject(objectType, object, fieldEntry)}. + * If {resolvedObject} is {null}, return {tuple(responseKey, null)}, + indicating that an entry exists in the result map whose value is `null`. + * Let {subSelectionSet} be the result of calling {MergeSelectionSets(fields)}. + * Let {responseValue} be the result of calling {CompleteValue(fieldType, resolvedObject, subSelectionSet)}. + * Return {tuple(responseKey, responseValue)}. + +GetFieldTypeFromObjectType(objectType, firstField): + * Call the method provided by the type system for determining the field type + on a given object type. + +ResolveFieldOnObject(objectType, object, firstField): + * Call the method provided by the type system for determining the resolution + of a field on a given object. + +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}. + +CompleteValue(fieldType, result, subSelectionSet): + + * 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, result)}. + * If {completedResult} is {null}, throw a field error. + * Return {completedResult}. + * If {result} is {null} or a value similar to {null} such as {undefined} or + {NaN}, return {null}. + * If {fieldType} is a List type: + * If {result} is not a collection of values, throw a field error. + * Let {innerType} be the inner type of {fieldType}. + * Return a list where each item is the result of calling + {CompleteValue(innerType, resultItem)}, where {resultItem} is each item + in {result}. + * If {fieldType} is a Scalar or Enum type: + * Return the result of "coercing" {result}, ensuring it is a legal value of + {fieldType}, otherwise {null}. + * If {fieldType} is an Object, Interface, or Union type: + * Return the result of evaluating {subSelectionSet} on {fieldType} normally. + +### Normal evaluation + +When evaluating a grouped field set without a serial execution order requirement, +the executor can determine the entries in the result map in whatever order it +chooses. Because the resolution of fields other than top-level mutation fields +is always side effect–free and idempotent, the execution order must not +affect the result, and hence the server has the freedom to evaluate the field +entries in whatever order it deems optimal. + +For example, given the following grouped field set to be evaluated normally: + +```graphql +{ + birthday { + month + }, + address { + street + } +} +``` + +A valid GraphQL executor can resolve the four fields in whatever order it +chose. + +### Serial execution + +Observe that based on the above sections, the only time an executor will run in +serial execution order is on the top level selection set of a mutation +operation and on its corresponding grouped field set. + +When evaluating 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 evaluated serially: + +```graphql +{ + changeBirthday(birthday: $newBirthday) { + month + }, + changeAddress(address: $newAddress) { + street + } +} +``` + +The executor must, in serial: + + - Run `getFieldEntry` for `changeBirthday`, which during `CompleteValue` will + evaluate the `{ month }` sub-selection set normally. + - Run `getFieldEntry` for `changeAddress`, which during `CompleteValue` will + evaluate 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: + +```graphql +{ + first: changeTheNumber(newNumber: 1) { + theNumber + }, + second: changeTheNumber(newNumber: 3) { + theNumber + }, + third: changeTheNumber(newNumber: 2) { + theNumber + }, +} +``` + +The executor will evaluate the following serially: + + - Resolve the `changeTheNumber(newNumber: 1)` field + - Evaluate the `{ theNumber }` sub-selection set of `first` normally + - Resolve the `changeTheNumber(newNumber: 3)` field + - Evaluate the `{ theNumber }` sub-selection set of `second` normally + - Resolve the `changeTheNumber(newNumber: 2)` field + - Evaluate the `{ theNumber }` sub-selection set of `third` normally + +A correct executor must generate the following result for that selection set: + +```js +{ + "first": { + "theNumber": 1 + }, + "second": { + "theNumber": 3 + }, + "third": { + "theNumber": 2 + }, +} +``` + +### Error handling + +If an error occurs when resolving a field, it should be treated as though +the field returned null, and an error must be added to the "errors" list +in the response. + +### Nullability + +If the result of resolving a field is null (either because the function to +resolve the field returned null or because an error occurred), and that +field is marked as being non-null in the type system, then the result +of evaluating the entire field set that contains this field is now +null. + +If the field was null because of an error, then the error has already been +logged, and the "errors" list in the response must not be affected. + +If the field resolution function returned null, and the field was non-null, +then no error has been logged, so an appropriate error must be added to +the "errors" list. diff --git a/Section 7 -- Response.md b/Section 7 -- Response.md new file mode 100644 index 000000000..92c89dae9 --- /dev/null +++ b/Section 7 -- Response.md @@ -0,0 +1,115 @@ +# Response + +When a GraphQL server receives a request, it must return a well-formed +response. The server's response describes the result of executing the requested +operation if successful, and describes any errors encountered during the +request. + +A response may contain both a partial response as well as encountered errors in +the case that an error occured on a field which was replaced with null. + +## Serialization Format + +GraphQL does not require a specific serialization format. However, clients +should use a serialization format that supports the major primitives in the +GraphQL response. In particular, the serialization format must support +representations of the following four primitives: + + * Map + * List + * String + * Null + +A serialization format may support the following primitives, however, strings +may be used as a substitute for those primitives. + + * Boolean + * Int + * Float + * Enum Value + +### JSON Serialization + +JSON is the preferred serialization format for GraphQL, though as noted above, +GraphQL does not require a specific serialization format. For consistency and +ease of notation, examples of the response are given in JSON throughout the +spec. In particular, in our JSON examples, we will represent primitives using +the following JSON concepts: + +| GraphQL Value | JSON Value | +| ------------- | ----------------- | +| Map | Object | +| List | Array | +| Null | {null} | +| String | String | +| Boolean | {true} or {false} | +| Int | Number | +| Float | Number | +| Enum Value | String | + + +## Response Format + +A response to a GraphQL operation must be a map. + +If the operation included execution, the response map must contain the an entry +with key "data". The value of this entry is described in the "Data" section. If +the operation failed before execution, due to a syntax error, missing +information, or validation error, this entry must not be present. + +If the operation encountered 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 operation completed without encountering any errors, 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 servers and +clients, the top level response map must not contain any entries other than the +three described above. + +### 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 schema's query root type; if the operation was a mutation, this +output will be an object of the schema's mutation root type. + +If an error was encountered before execution begins, the `data` entry should +not be present in the result. + +If an error was encountered 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 encountered during the requested operation, the `errors` +entry should not be present in the result. + +Every error must contain an entry with the key `message` with a string +description of the error intended for the developer as a guide to understand +and correct the error. + +If an error can be associated to a particular point in the requested GraphQL +document, it should contain an entry with the key `locations` with a list of +locations, where each location is a map with the keys `line` and `column`, both +positive numbers starting from `1` which describe the beginning of an +associated syntax element. + +GraphQL servers may provide additional entries to error as they choose to +produce more helpful or machine-readable errors, however future versions of the +spec may describe additional entries to errors. + +If the `data` entry in the response is `null` or 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 `null`, the `errors` entry in the +response may contain any errors that occurred during execution. If errors +occurred during execution, it should contain those errors. diff --git a/Section 8 -- Grammar.md b/Section 8 -- Grammar.md new file mode 100644 index 000000000..9407f985a --- /dev/null +++ b/Section 8 -- Grammar.md @@ -0,0 +1,362 @@ +# Grammar + +A GraphQL document is defined in a syntactic grammar where terminal symbols are +tokens. Tokens are defined in a lexical grammar which matches patterns of source +characters. The result of parsing a sequence of source UTF-8 characters produces +a GraphQL AST. + +Symbols are defined (ex. Symbol :) as either one sequence of symbols or a list +of possible sequences of symbols, either as a bulleted list or using the +"one of" short hand. + +A subscript suffix "{Symbol?}" is shorthand for two possible sequences, one +including that symbol and one excluding it. + +As an example: + +Sentence : Noun Verb Adverb? + +is shorthand for + +Sentence : + - Noun Verb + - Noun Verb Adverb + +A subscript suffix "{Symbol+}" is shorthand for a list of +one or more of that symbol. + +As an example: + +Book : Cover Page+ Cover + +is shorthand for + +Book : Cover Page_list Cover + +Page_list : + - Page + - Page_list Page + +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 + +is shorthand for + +Example : + - A + - B_param + - C + - E + +Example_param : + - A + - B_param + - C_param + - D + + +## Tokens + +A GraphQL document is comprised of several kinds of source tokens defined here +in a lexical grammar and used as terminal symbols in GraphQL's syntactic +grammar. This lexical grammar defines patterns of source characters by specifing +character patterns in {`monospace`} or as {/regular_expressions/}. Non-terminal +patterns are defined as {Italics}. + +The GraphQL document syntactic grammar is defined in terms of these +lexical tokens: + +Token : + - Punctuator + - Name + - IntValue + - FloatValue + - StringValue + +Punctuator : one of ! $ ( ) ... : = @ [ ] { | } + +Name : /[_A-Za-z][_0-9A-Za-z]*/ + +IntValue : Sign? IntegerPart + +FloatValue : Sign? IntegerPart . Digit+ ExponentPart? + +Sign : - + +IntegerPart : + - 0 + - NonZeroDigit + - NonZeroDigit Digit+ + +ExponentPart : e Sign? Digit+ + +NonZeroDigit : one of 1 2 3 4 5 6 7 8 9 + +Digit : + - 0 + - NonZeroDigit + +StringValue : + - `""` + - `"` StringCharacter+ `"` + +StringCharacter : + - "Any character" but not `"` or \ or LineTerminator + - \ EscapedUnicode + - \ EscapedCharacter + +EscapedUnicode : u /[0-9A-Fa-f]{4}/ + +EscapedCharacter : one of `"` \ `/` b f n r t + + + +### Ignored Source + +Before every lexical token may be any amount of ignored source characters +such as whitespace and comments. Whitespace, other than within strings, is +not significant. + +GraphQL treats comma `,` similarly to whitespace. This ensures that the absense +or presence of a comma does not meaningfully alter the interpretted syntax of +the document which is a common user-error in other languages. It also allows for +the stylistic use of trailing commas or line-breaks as delimiters which are +often desired for legibility and maintainability of source code. The use of +commas and other whitespace is suggested when it improves legibility. + +GraphQL ignores these character sequences: + +Ignored : + - WhiteSpace + - LineTerminator + - Comment + - , + +WhiteSpace : + - "Horizontal Tab (U+0009)" + - "Vertical Tab (U+000B)" + - "Form Feed (U+000C)" + - "Space (U+0020)" + - "No-break Space (U+00A0)" + +LineTerminator : + - "New Line (U+000A)" + - "Carriage Return (U+000D)" + - "Line Separator (U+2028)" + - "Paragraph Separator (U+2029)" + +Comment : + - `#` CommentChar+? + +CommentChar : "Any character" but not LineTerminator + + + +## Syntax + +A GraphQL document is defined in a syntactic grammar where terminal symbols are +expressed as either an italicized token (ex. {Document}) or as +monospaced short-hand for a {Punctuator} (ex. {`:`}) or {Name} +(ex. {`query`}). + + +### Document + +A GraphQL document describes a complete file or request string. A document +contains multiple definitions including an Operation. + +Document : Definition+ + +Definition : + - OperationDefinition + - FragmentDefinition + - TypeExtension + - TypeDefinition + - EnumDefinition + + +### Operations + +An operation describes some type of request to GraphQL. The most common +operation is a `query`, a read-only request for data from GraphQL. A short-hand +syntax exists for a query operation. + +OperationDefinition : + - SelectionSet + - OperationType Name VariableDefinitions? Directives? SelectionSet + +OperationType : one of query mutation + +VariableDefinitions : ( VariableDefinition+ ) + +VariableDefinition : Variable : Type DefaultValue? + +Variable : $ Name + +DefaultValue : = Value[Const] + +SelectionSet : { Selection+ } + +Selection : + - Field + - FragmentSpread + - InlineFragment + +Field : Alias? Name Arguments? Directives? SelectionSet? + +Alias : Name : + +Arguments : ( Argument+ ) + +Argument : Name : Value + + +### Fragments + +Fragments allow for the reuse of common selections of fields, reducing +duplicated text in the document. Inline fragments can be used directly inline a +selection to apply a type condition when querying against an interface or union. + +FragmentSpread : ... FragmentName Directives? + +InlineFragment : ... on TypeCondition Directives? SelectionSet + +FragmentDefinition : fragment FragmentName on TypeCondition Directives? SelectionSet + +FragmentName : [ lookahead ! on ] Name + +TypeCondition : TypeName + + +### Values + +Fields may take values for arguments. A value may be any JSON-style value, +a variable or an Enum value. + +Value[Const] : + - [~Const] Variable + - IntValue + - FloatValue + - StringValue + - BooleanValue + - EnumValue + - ArrayValue[?Const] + - ObjectValue[?Const] + +BooleanValue : + - true + - false + +EnumValue : Name + + +#### Array Value + +ArrayValue[Const] : + - [ ] + - [ Value[?Const]+ ] + +**Semantics** + +ArrayValue : [ ] + + * Return a new empty list value. + +ArrayValue : [ 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} + + +#### Object Value + +ObjectValue[Const] : + - { } + - { ObjectField[?Const]+ } + +ObjectField[Const] : Name : Value[?Const] + +**Semantics** + +ObjectValue : { } + + * 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}. + * If {inputObject} contains a field named {name} throw Syntax Error. + * Let {value} be the result of evaluating {Value} in {field}. + * Add a field to {inputObject} of name {name} containing value {value}. + * Return {inputObject} + + +### Directives + +Directives provide a way to describe runtime execution and type validation +behavior in a GraphQL document. + +Directives : Directive+ + +Directive : + - @ Name + - @ Name : Value + + +### Types + +GraphQL describes the schema of the data it provides using a type system. These +types are referred to in the document when defining query variables. + +Type : + - TypeName + - ListType + - NonNullType + +TypeName : Name + +ListType : [ Type ] + +NonNullType : + - TypeName ! + - ListType ! + +**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} + +Type : [ 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} diff --git a/package.json b/package.json new file mode 100644 index 000000000..ec664f883 --- /dev/null +++ b/package.json @@ -0,0 +1,25 @@ +{ + "name": "graphql-spec", + "private": true, + "contributors": [ + "Lee Byron (http://leebyron.com/)", + "Nicholas Schrock ", + "Daniel Schafer " + ], + "license": "BSD", + "homepage": "https://facebook.github.io/graphql", + "bugs": { + "url": "https://github.com/facebook/graphql/issues" + }, + "repository": { + "type": "git", + "url": "http://github.com/facebook/graphql.git" + }, + "scripts": { + "test": "spec-md GraphQL.md > /dev/null", + "build": "mkdir -p out; cp node_modules/spec-md/css/* out; spec-md GraphQL.md > out/index.html" + }, + "devDependencies": { + "spec-md": "0.3.7" + } +}