An introduction to GraphQL
By Flavio Copes
Learn what GraphQL is and how to write queries with fields, arguments, variables, aliases, fragments, directives, and mutations.
GraphQL is a query language for APIs and a specification for executing those queries.
A GraphQL API describes its data with a typed schema. The client asks for the fields it needs, and the response follows the shape of that request.
GraphQL is not a database and it is not tied to a particular programming language or transport protocol. You can put it in front of SQL, a document database, other APIs, or any combination of data sources.
The schema defines what you can ask for
Here is a small schema written with the GraphQL Schema Definition Language:
type Query {
person(id: ID!): Person
}
type Person {
id: ID!
name: String!
age: Int
friends(limit: Int): [Person!]!
}
The Query type is the entry point for read operations. It says that the client can ask for a person by ID.
The ! after a type means that the value cannot be null.
Write a query
A query contains a selection set: the fields you want the server to return.
query GetPerson {
person(id: "42") {
name
age
}
}
The response has a top-level data key and mirrors the selected fields:
{
"data": {
"person": {
"name": "Roger",
"age": 28
}
}
}
If something goes wrong, the response can also contain a top-level errors array. A response may contain both partial data and errors, so clients should account for that possibility.
Naming operations, as in GetPerson, makes requests easier to identify in logs and developer tools.
Fields and arguments
Fields can have arguments. In the previous query, id is an argument of the person field.
Arguments can also appear on nested fields:
query GetPerson {
person(id: "42") {
name
friends(limit: 3) {
name
}
}
}
The available fields and arguments come from the server’s schema. You cannot request arbitrary properties.
Use variables for dynamic values
Do not build queries by interpolating user input into a string. Declare variables in the operation and send their values separately:
query GetPerson($personId: ID!) {
person(id: $personId) {
name
age
}
}
The variables are a separate JSON object:
{
"personId": "42"
}
Variables are validated against their declared types before the operation runs.
You can give an optional variable a default value:
query GetPerson($personId: ID = "42") {
person(id: $personId) {
name
}
}
Rename fields with aliases
Aliases let you request the same field more than once with different arguments:
query ComparePeople {
first: person(id: "1") {
name
}
second: person(id: "2") {
name
}
}
The response keys will be first and second.
Reuse fields with fragments
A fragment is a reusable selection set:
fragment PersonSummary on Person {
id
name
age
}
query ComparePeople {
first: person(id: "1") {
...PersonSummary
}
second: person(id: "2") {
...PersonSummary
}
}
Fragments are useful when several parts of an interface need the same fields.
Include fields conditionally
The GraphQL specification includes the @include and @skip directives. They can conditionally include a field or fragment:
query GetPerson($personId: ID!, $withAge: Boolean!) {
person(id: $personId) {
name
age @include(if: $withAge)
}
}
When withAge is false, the response does not contain the age field.
Queries, mutations, and subscriptions
GraphQL has three operation types:
queryreads datamutationchanges data or triggers a side effectsubscriptionreceives updates over time
A mutation still asks for a selection set:
mutation RenamePerson($id: ID!, $name: String!) {
renamePerson(id: $id, name: $name) {
id
name
}
}
The schema determines which mutation fields are available and what they return.
Sending GraphQL over HTTP
GraphQL itself does not require HTTP, but HTTP is the most common transport. A service typically exposes one endpoint such as /graphql.
Query and mutation operations can be sent with POST. The JSON request body commonly contains query, variables, and optionally operationName:
const response = await fetch('/graphql', {
method: 'POST',
headers: {
'content-type': 'application/json',
accept: 'application/graphql-response+json',
},
body: JSON.stringify({
query: `
query GetPerson($personId: ID!) {
person(id: $personId) {
name
}
}
`,
variables: {
personId: '42',
},
operationName: 'GetPerson',
}),
})
const result = await response.json()
Servers may also accept GET for query operations. Mutations must use POST.
For more details, see the official guides to GraphQL queries, schemas and types, and serving GraphQL over HTTP.
Related posts about graphql: