GraphQL is a query language for your API, and a server-side runtime for executing queries by using a type system you define for your data.
A GraphQL service is created by defining types and fields on those types, then providing functions for each field on each type.
1 | type Query { |
Along with functions for each field on each type:
1 | function Query_me(request) { |
Once a GraphQL service is running (typically at a URL on a web service), it can be sent GraphQL queries to validate and execute. A received query is first checked to ensure it only refers to the types and fields defined, then runs the provided functions to produce a result.
For example the query:
1 | { |
Fields
GraphQL is about asking for specific fields on objects.
GraphQL queries can traverse related objects and their fields, letting clients fetch lots of related data in one request, instead of making several roundtrips as one would need in a classic RESTFul architecture.
Arguments
In GraphQL, every field and nested object can get its own set of arguments, making GraphQL a complete replacement for making multiple API fetches. You can even pass arguments into scalar fields, to implement data transformations once on the server, instead of on every separately.
1 | { |
Arguments can be of many different types. GraphQL comes with a default set of types, but a GraphQL server can also declare its own custom types, as long as they can be serialized into your transport format.
Aliases
You can’t directly query for the same field with different arguments, so you need alias to rename the result
1 | { |
In the above example, the two hero
fields would have conflicted, but since we can alias them to different names, we can get both results in one request.
Fragments
GraphQL includes reusable units called fragments. Fragments let you construct sets of fields, and then include them in queries where you need to.
1 | { |
Variables
When we start working with variables, we need to do three things:
Replace the static values in the query with
$variableName
Declare
$variableName
as one of the variables accepted by the queryPass
variableName: value
in the separate, transport-specific(usually JSON) variables dictionary
1 | query HeroNameAndFriends($episode: Episode) { |
Now in our client code, we can simply pass a different variable rather than needing to construct an entirely new query.
Variable Definitions
The variable definitions are the part that looks like ($episode: Episode)
in the query above. It works just like the argument definition for a function in a types language. It lists all of the variables, prefixed by $
, followed by their type, in this case Episode
.
All declared variables must be either scalars, enums, or input object types.
Variable Definition can be optional or required. In the case above, since there isn’t an !
next to the Episode
type, it’s optional. But if the field you are passing the variable into requires a non-null argument, then the variable must be required.
Default Variables
Default variables can also be assigned to the variables in the query by adding the default value after the type declaration.
1 | query HeroNameAndFriends($episode: Episode = 'JEDI') { |
Directives
1 | query Hero($episode: Episode, $withFriends: Boolean!) { |
We needed to use a new feature in GraphQL called directive. A directive can be attached to a field or fragment inclusion, and can affect execution of the query in any way the server desires. The core GraphQL specification includes exactly two directives, which must be supported by any spec-compliant GraphQL server implementation:
@include(if: Boolean): Only include this field in the result if the argument is
true
@skip(if: Boolean): Skip this field if the argument is
true
Mutations
If mutation field returns an object type, you can ask for nested fields. This can be useful for fetching the new state of an object after an update.
1 | mutation CreateReviewForEpisode ($ep: Episode!, $review: ReviewInput!) { |
Here the createReview
returns the star
and commentary
fields of the newly created review.
Multiple fields in mutations
A mutation can contain multiple fields, just like a query. There’s one important distinction between queries and mutations, other than the name.
While query fields are executed in parallel, mutation fields run in series, one after the other. This means that if we send two incrementCredits
mutations in one request, the first is guaranteed to finished before the second begins, ensuring that we don’t end up with a race condition with ourselves.
Inline Fragments
GraphQL include the ability to define interfaces and union types.
If you are querying a field that returns an interface or a union type, you will need to use inline fragments to access data on the underlaying concrete type.
1 | query HeroForEpisode ($ep: Episode!) { |
In this query, the hero
field returns the type Character
, which might be either a Human
or a Droid
depending on the episode
argument. In the direct selection, you can ask for fields that exists on the Character
interface such as name
.
To ask for a field on the concrete type, you need to use a inline fragment with a type condition. Because the first fragment is labeled as ... on Droid
, the primaryFunction
field will only be executed if the Character
returned from hero
is of the Droid
type.
Named fragments can also be used in the same way, since a named fragment always has a type attached.
Meta fields
Given that there are some situations where you don’t know what type you will get back from the GraphQL service, you need some way to determine how to handle that data on the client. GraphQL allows you to request __typename
, a meta field, at any point in a query to get the name of the object type at that point.
1 | { |
In the above query, search
returns a union type that can be one of three options. It would be impossible to tell apart the different types from the client without the __typename
field.
Schemas and Types
Type System
GraphQL query language is basically about selecting fields on objects.
1 | { |
We start with a special ‘root’ object
We select the
hero
field on thatFor the object returned by
hero
, we select thename
andappearsIn
fields
Object Types and Fields
The most basic components of a GraphQL schema are object types, which just represent a kind of object you can fetch from your service, and what fields it has. In the GraphQL schema language, we might represent it like this:
1 | type Character { |
Character
is a GraphQL Type, meaning it’s a type with some fields. Most of the types in your schema will be object types.name
andappearsIn
are fields on theCharacter
type. That means thatname
andappearsIn
are the only fields that can appear in any part of a GraphQL query that operates on theCharacter
type.String
is one of the built-in scalar typesString!
means that the field isnon-nullable
, meaning that the GraphQL service promises to always give you a value when you query this field.[Episode]!
represents an array ofEpisode
objects. Since it is also non-nullable, you can always expect an array ( with zero or more items) when you query theappearsIn
field.
Arguments
Every field on a GraphQL object type can have zero or more arguments, for example the length
field below:
1 | type Starship { |
All arguments are named.
The Query and Mutation Types
Most types in schema will just be normal object types, but there are two types that are special within a schema:
1 | schema { |
Every GraphQL service has a query
type and may not have a mutation
type. These types are the same as a regular object type, but they are special because they define the entry point of every GraphQL query.
1 | query { |
That means that the GraphQL service needs to have a Query
type with hero
and droid
fields:
1 | type Query { |
Mutations work in a similar way - you define fields on the Mutation
type, and those are available as the root mutation fields you can call in your query.
Scalar Types
Int
: A signed 32-bit integerFloat
: A signed double-precision floating-point valueString
: A UTF-8 character sequenceBoolean
: true or falseID
: The ID Scalar type represents a unique identifier, often used to refetch an object or as the key for a cache. The ID types serialized in the same way as a String; However, defining it as anID
signifies that it is not intended to be human-readable.
In most GraphQL service implementation, there is also a way to specify custom scalar types.
1 | scalar Date |
Enumeration Types
Also called Enums, enumeration types are a special kind of scalar that is restricted to a particular set of allowed values.
1 | enum Episode { |
This means that wherever we use the type Episode
in our schema, we expect it to be exactly one of NEWHOPE
, EMPIRE
, JEDI
.
Lists and Non-Null
Object types, scalars, and enums are the only kinds of types you can define in GraphQL, but when you use the types in other parts of the schema, or in your query variable declarations, you can apply additional type modifier that affects validation of those values.
Interfaces
An Interface is an abstract type that includes a certain set of fields that a type must include to implement the interface.
1 | // define |
Union Types
Union Types are similar to interfaces, but they don’t get to specify any common fields between the types.
1 | union SearchResult = Human | Droid | Starship |
Input Types
In GraphQL schema language, input types look exactly the same as regular object types, but with the keyword input
instead of type
:
1 | input ReviewInput { |
1 | mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) { |