Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

GraphQL is a query language and execution engine tied to any backend service.

NotificationsYou must be signed in to change notification settings

graphql/graphql-spec

Repository files navigation

GraphQLConf 2024 Banner: September 10-12, San Francisco. Hosted by the GraphQL Foundation

GraphQL

GraphQL Logo

The GraphQL specification is edited in the markdown files found in/spec the latest release of which is published athttps://graphql.github.io/graphql-spec/.

The latest draft specification can be found athttps://graphql.github.io/graphql-spec/draft/ which tracks the latest commit tothe main branch in this repository.

Previous releases of the GraphQL specification can be found at permalinks thatmatch theirrelease tag. Forexample,https://graphql.github.io/graphql-spec/October2016/. If you are linkingdirectly to the GraphQL specification, it's best to link to a tagged permalinkfor the particular referenced version.

Overview

This is a Working Draft of the Specification for GraphQL, a query language forAPIs created by Facebook.

The target audience for this specification is not the client developer, butthose who have, or are actively interested in, building their own GraphQLimplementations and tools.

In order to be broadly adopted, GraphQL will have to target a wide variety ofbackend environments, frameworks, and languages, which will necessitate acollaborative effort across projects and organizations. This specificationserves as a point of coordination for this effort.

Looking for help? Find resourcesfrom the community.

Getting Started

GraphQL consists of a type system, query language and execution semantics,static validation, and type introspection, each outlined below. To guide youthrough each of these components, we've written an example designed toillustrate the various pieces of GraphQL.

This example is not comprehensive, but it is designed to quickly introduce thecore concepts of GraphQL, to provide some context before diving into the moredetailed specification or theGraphQL.js reference implementation.

The premise of the example is that we want to use GraphQL to query forinformation 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 ofobjects it can return, described in a GraphQL type system and returned in theGraphQL Schema.

For our Star Wars example, thestarWarsSchema.tsfile in GraphQL.js defines this type system.

The most basic type in the system will beHuman, representing characters likeLuke, Leia, and Han. All humans in our type system will have a name, so wedefine theHuman type to have a field called "name". This returns a String,and we know that it is not null (since allHumans have a name), so we willdefine the "name" field to be a non-nullable String. Using a shorthand notationthat we will use throughout the spec and documentation, we would describe thehuman type as:

typeHuman {name:String}

This shorthand is convenient for describing the basic shape of a type system;the JavaScript implementation is more full-featured, and allows types and fieldsto be documented. It also sets up the mapping between the type system and theunderlying data; for a test case in GraphQL.js, the underlying data is aset of JavaScript objects,but in most cases the backing data will be accessed through some service, andthis type system layer will be responsible for mapping from types and fields tothat service.

A common pattern in many APIs, and indeed in GraphQL is to give objects an IDthat 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.

typeHuman {id:Stringname:StringhomePlanet:String}

Since we're talking about the Star Wars trilogy, it would be useful to describethe episodes in which each character appears. To do so, we'll first define anenum, which lists the three episodes in the trilogy:

enumEpisode {  NEWHOPE  EMPIRE  JEDI}

Now we want to add a field toHuman describing what episodes they were in.This will return a list ofEpisodes:

typeHuman {id:Stringname:StringappearsIn: [Episode]homePlanet:String}

Now, let's introduce another type,Droid:

typeDroid {id:Stringname:StringappearsIn: [Episode]primaryFunction:String}

Now we have two types! Let's add a way of going between them: humans and droidsboth have friends. But humans can be friends with both humans and droids. How dowe refer to either a human or a droid?

If we look, we note that there's common functionality between humans and droids;they both have IDs, names, and episodes in which they appear. So we'll add aninterface,Character, and make bothHuman andDroid implement it. Once wehave that, we can add thefriends field, that returns a list ofCharacters.

Our type system so far is:

enumEpisode {  NEWHOPE  EMPIRE  JEDI}interfaceCharacter {id:Stringname:Stringfriends: [Character]appearsIn: [Episode]}typeHumanimplementsCharacter {id:Stringname:Stringfriends: [Character]appearsIn: [Episode]homePlanet:String}typeDroidimplementsCharacter {id:Stringname:Stringfriends: [Character]appearsIn: [Episode]primaryFunction:String}

One question we might ask, though, is whether any of those fields can returnnull. By default,null is a permitted value for any type in GraphQL, sincefetching data to fulfill a GraphQL query often requires talking to differentservices that may or may not be available. However, if the type system canguarantee that a type is never null, then we can mark it as Non Null in the typesystem. We indicate that in our shorthand by adding an "!" after the type. Wecan update our type system to note that theid is never null.

Note that while in our current implementation, we can guarantee that more fieldsare non-null (since our current implementation has hard-coded data), we didn'tmark them as non-null. One can imagine we would eventually replace our hardcodeddata with a backend service, which might not be perfectly reliable; by leavingthese fields as nullable, we allow ourselves the flexibility to eventuallyreturn null to indicate a backend error, while also telling the client that theerror occurred.

enumEpisode {  NEWHOPE  EMPIRE  JEDI}interfaceCharacter {id:String!name:Stringfriends: [Character]appearsIn: [Episode]}typeHumanimplementsCharacter {id:String!name:Stringfriends: [Character]appearsIn: [Episode]homePlanet:String}typeDroidimplementsCharacter {id:String!name:Stringfriends: [Character]appearsIn: [Episode]primaryFunction:String}

We're missing one last piece: an entry point into the type system.

When we define a schema, we define an object type that is the basis for allquery operations. The name of this type isQuery by convention, and itdescribes our public, top-level API. OurQuery type for this example will looklike this:

typeQuery {hero(episode:Episode):Characterhuman(id:String!):Humandroid(id:String!):Droid}

In this example, there are three top-level operations that can be done on ourschema:

  • hero returns theCharacter who is the hero of the Star Wars trilogy; ittakes an optional argument that allows us to fetch the hero of a specificepisode instead.
  • human accepts a non-null string as a query argument, a human's ID, andreturns the human with that ID.
  • droid does the same for droids.

These fields demonstrate another feature of the type system, the ability for afield to specify arguments that configure their behavior.

When we package the whole type system together, defining theQuery type aboveas our entry point for queries, this creates a GraphQL Schema.

This example just scratched the surface of the type system. The specificationgoes into more detail about this topic in the "Type System" section, and thetype directory inGraphQL.js contains code implementing a specification-compliant GraphQL typesystem.

Query Syntax

GraphQL queries declaratively describe what data the issuer wishes to fetch fromwhoever is fulfilling the GraphQL query.

For our Star Wars example, thestarWarsQueryTests.jsfile 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 ofsample data, located instarWarsData.js.This test file can be run to exercise the reference implementation.

An example query on the above schema would be:

queryHeroNameQuery {hero {name  }}

The initial line,query HeroNameQuery, defines a query with the operation nameHeroNameQuery that starts with the schema's root query type; in this case,Query. As defined above,Query has ahero field that returns aCharacter, so we'll query for that.Character then has aname field thatreturns aString, so we query for that, completing our query. The result ofthis query would then be:

{"hero": {"name":"R2-D2"  }}

Specifying thequery keyword and an operation name is only required when aGraphQL document defines multiple operations. We therefore could have writtenthe previous query with the query shorthand:

{hero {name  }}

Assuming that the backing data for the GraphQL server identified R2-D2 as thehero. The response continues to vary based on the request; if we asked forR2-D2's ID and friends with this query:

queryHeroNameAndFriendsQuery {hero {idnamefriends {idname    }  }}

then we'll get back a response like this:

{"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 abovequery, we asked for R2-D2's friends, but we can ask for more information abouteach of those objects. So let's construct a query that asks for R2-D2's friends,gets their name and episode appearances, then asks for each oftheir friends.

queryNestedQuery {hero {namefriends {nameappearsInfriends {name      }    }  }}

which will give us the nested response

{"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" }        ]      }    ]  }}

TheQuery type above defined a way to fetch a human given their ID. We can useit by hard-coding the ID in the query:

queryFetchLukeQuery {human(id:"1000") {name  }}

to get

{"human": {"name":"Luke Skywalker"  }}

Alternately, we could have defined the query to have a query parameter:

queryFetchSomeIDQuery($someId:String!) {human(id:$someId) {name  }}

This query is now parameterized by$someId; to run it, we must provide thatID. 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 getnull back forthehuman, indicating that no such object exists.

Notice that the key in the response is the name of the field, by default. It issometimes useful to change this key, for clarity or to avoid key collisions whenfetching the same field with different arguments.

We can do that with field aliases, as demonstrated in this query:

queryFetchLukeAliased {luke:human(id:"1000") {name  }}

We aliased the result of thehuman field to the keyluke. Now the responseis:

{"luke": {"name":"Luke Skywalker"  }}

Notice the key is "luke" and not "human", as it was in our previous examplewhere we did not use the alias.

This is particularly useful if we want to use the same field twice withdifferent arguments, as in the following query:

queryFetchLukeAndLeiaAliased {luke:human(id:"1000") {name  }leia:human(id:"1003") {name  }}

We aliased the result of the firsthuman field to the keyluke, and thesecond toleia. So the result will be:

{"luke": {"name":"Luke Skywalker"  },"leia": {"name":"Leia Organa"  }}

Now imagine we wanted to ask for Luke and Leia's home planets. We could do sowith this query:

queryDuplicateFields {luke:human(id:"1000") {namehomePlanet  }leia:human(id:"1003") {namehomePlanet  }}

but we can already see that this could get unwieldy, since we have to add newfields to both parts of the query. Instead, we can extract out the common fieldsinto a fragment, and include the fragment in the query, like this:

queryUseFragment {luke:human(id:"1000") {...HumanFragment  }leia:human(id:"1003") {...HumanFragment  }}fragmentHumanFragmentonHuman {namehomePlanet}

Both of those queries give this result:

{"luke": {"name":"Luke Skywalker","homePlanet":"Tatooine"  },"leia": {"name":"Leia Organa","homePlanet":"Alderaan"  }}

TheUseFragment andDuplicateFields queries will both get the same result,butUseFragment is less verbose; if we wanted to add more fields, we could addit 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 theoutput; the query can ask for that type using the special field__typename,defined on every object.

queryCheckTypeOfR2 {hero {__typenamename  }}

Since R2-D2 is a droid, this will return

{"hero": {"__typename":"Droid","name":"R2-D2"  }}

This was particularly useful becausehero was defined to return aCharacter,which is an interface; we might want to know what concrete type was actuallyreturned. If we instead asked for the hero of Episode V:

queryCheckTypeOfLuke {hero(episode: EMPIRE) {__typenamename  }}

We would find that it was Luke, who is a Human:

{"hero": {"__typename":"Human","name":"Luke Skywalker"  }}

As with the type system, this example just scratched the surface of the querylanguage. The specification goes into more detail about this topic in the"Language" section, and thelanguagedirectory in GraphQL.js contains code implementing a specification-compliantGraphQL query language parser and lexer.

Validation

By using the type system, it can be predetermined whether a GraphQL query isvalid or not. This allows servers and clients to effectively inform developerswhen an invalid query has been created, without having to rely on runtimechecks.

For our Star Wars example, the filestarWarsValidationTests.jscontains a number of demonstrations of invalid operations, and is a test filethat can be run to exercise the reference implementation's validator.

To start, let's take a complex valid query. This is theNestedQuery examplefrom the above section, but with the duplicated fields factored out into afragment:

queryNestedQueryWithFragment {hero {...NameAndAppearancesfriends {...NameAndAppearancesfriends {...NameAndAppearances      }    }  }}fragmentNameAndAppearancesonCharacter {nameappearsIn}

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 giventype. So ashero returns aCharacter, we have to query for a field onCharacter. That type does not have afavoriteSpaceship field, so this query:

# INVALID: favoriteSpaceship does not exist on CharacterqueryHeroSpaceshipQuery {hero {favoriteSpaceship  }}

is invalid.

Whenever we query for a field and it returns something other than a scalar or anenum, we need to specify what data we want to get back from the field. Heroreturns aCharacter, and we've been requesting fields likename andappearsIn on it; if we omit that, the query will not be valid:

# INVALID: hero is not a scalar, so fields are neededqueryHeroNoFieldsQuery {hero}

Similarly, if a field is a scalar, it doesn't make sense to query for additionalfields on it, and doing so will make the query invalid:

# INVALID: name is a scalar, so fields are not permittedqueryHeroFieldsOnScalarQuery {hero {name {firstCharacterOfName    }  }}

Earlier, it was noted that a query can only query for fields on the type inquestion; when we query forhero which returns aCharacter, we can onlyquery for fields that exist onCharacter. What happens if we want to query forR2-D2s primary function, though?

# INVALID: primaryFunction does not exist on CharacterqueryDroidFieldOnCharacter {hero {nameprimaryFunction  }}

That query is invalid, becauseprimaryFunction is not a field onCharacter.We want some way of indicating that we wish to fetchprimaryFunction if theCharacter is aDroid, and to ignore that field otherwise. We can use thefragments we introduced earlier to do this. By setting up a fragment defined onDroid and including it, we ensure that we only query forprimaryFunctionwhere it is defined.

queryDroidFieldInFragment {hero {name...DroidFields  }}fragmentDroidFieldsonDroid {primaryFunction}

This query is valid, but it's a bit verbose; named fragments were valuable abovewhen we used them multiple times, but we're only using this one once. Instead ofusing a named fragment, we can use an inline fragment; this still allows us toindicate the type we are querying on, but without naming a separate fragment:

queryDroidFieldInInlineFragment {hero {name...onDroid {primaryFunction    }  }}

This has just scratched the surface of the validation system; there are a numberof validation rules in place to ensure that a GraphQL query is semanticallymeaningful. The specification goes into more detail about this topic in the"Validation" section, and thevalidationdirectory in GraphQL.js contains code implementing a specification-compliantGraphQL validator.

Introspection

It's often useful to ask a GraphQL schema for information about what queries itsupports. GraphQL allows us to do so using the introspection system!

For our Star Wars example, the filestarWarsIntrospectionTests.jscontains a number of queries demonstrating the introspection system, and is atest file that can be run to exercise the reference implementation'sintrospection system.

We designed the type system, so we know what types are available, but if wedidn't, we can ask GraphQL, by querying the__schema field, always availableon the root type of a Query. Let's do so now, and ask what types are available.

queryIntrospectionTypeQuery {__schema {types {name    }  }}

and we get back:

{"__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 wedefined in our type system.
  • String, Boolean - These are built-in scalars that the type systemprovided.
  • __Schema,__Type,__TypeKind,__Field,__InputValue,__EnumValue,__Directive - These all are preceded with a doubleunderscore, indicating that they are part of the introspection system.

Now, let's try and figure out a good place to start exploring what queries areavailable. When we designed our type system, we specified what type all querieswould start at; let's ask the introspection system about that!

queryIntrospectionQueryTypeQuery {__schema {queryType {name    }  }}

and we get back:

{"__schema": {"queryType": {"name":"Query"    }  }}

And that matches what we said in the type system section, that theQuery typeis where we will start! Note that the naming here was just by convention; wecould have named ourQuery type anything else, and it still would have beenreturned here if we had specified it as the starting type for queries. Naming itQuery, though, is a useful convention.

It is often useful to examine one specific type. Let's take a look at theDroid type:

queryIntrospectionDroidTypeQuery {__type(name:"Droid") {name  }}

and we get back:

{"__type": {"name":"Droid"  }}

What if we want to know more about Droid, though? For example, is it aninterface or an object?

queryIntrospectionDroidKindQuery {__type(name:"Droid") {namekind  }}

and we get back:

{"__type": {"name":"Droid","kind":"OBJECT"  }}

kind returns a__TypeKind enum, one of whose values isOBJECT. If we askedaboutCharacter instead:

queryIntrospectionCharacterKindQuery {__type(name:"Character") {namekind  }}

and we get back:

{"__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 theintrospection system aboutDroid:

queryIntrospectionDroidFieldsQuery {__type(name:"Droid") {namefields {nametype {namekind      }    }  }}

and we get back:

{"__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 onDroid!

id looks a bit weird there, it has no name for the type. That's because it's a"wrapper" type of kindNON_NULL. If we queried forofType on that field'stype, we would find theString type there, telling us that this is a non-nullString.

Similarly, bothfriends andappearsIn have no name, since they are theLIST wrapper type. We can query forofType on those types, which will tellus what these are lists of.

queryIntrospectionDroidWrappedFieldsQuery {__type(name:"Droid") {namefields {nametype {namekindofType {namekind        }      }    }  }}

and we get back:

{"__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 fortooling; let's ask the system for documentation!

queryIntrospectionDroidDescriptionQuery {__type(name:"Droid") {namedescription  }}

yields

{"__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 queryfor enum values, what interfaces a type implements, and more. We can evenintrospect on the introspection system itself. The specification goes into moredetail about this topic in the "Introspection" section, and theintrospectionfile in GraphQL.js contains code implementing a specification-compliant GraphQLquery introspection system.

Additional Content

This README walked through the GraphQL.js reference implementation's typesystem, query execution, validation, and introspection systems. There's more inbothGraphQL.js and specification,including a description and implementation for executing queries, how to formata response, explaining how a type system maps to an underlying implementation,and how to format a GraphQL response, as well as the grammar for GraphQL.

Contributing to this repo

This repository is managed by EasyCLA. Project participants must sign the free(GraphQL Specification Membership agreementbefore making a contribution. You only need to do this one time, and it can besigned byindividual contributors ortheiremployers.

To initiate the signature process please open a PR against this repo. TheEasyCLA bot will block the merge if we still need a membership agreement fromyou.

You can finddetailed information here.If you have issues, please emailoperations@graphql.org.

If your company benefits from GraphQL and you would like to provide essentialfinancial support for the systems and people that power our community, pleasealso consider membership in theGraphQL Foundation.

About

GraphQL is a query language and execution engine tied to any backend service.

Resources

Security policy

Stars

Watchers

Forks

Packages

No packages published

Languages


[8]ページ先頭

©2009-2025 Movatter.jp