GraphQL 101


2017-07-23 · 5 min read

GraphQL stands for Graph Query Language. It allows the creation and consumption of APIs. It provides an interface between the client and the server for data fetching and manipulations (client-server model). It is an alternative to REST and web services. It designates both the query language and the runtime.

Facebook created GraphQL in 2012. It was meant to improve mobile applications. GraphQL was open sourced in 2015.

The language allows clients to define the exact shape of required data (GraphQL is declarative). It means you can selectively define fields you are interested in, and only those will be returned from the server. This avoids both the problems of over-fetching as well as under-fetching of data.

GraphQL is hierarchical. It can follow relationships between entities. A single GraphQL query can return related entities.

GraphQL is strongly typed. It provides an additional way (beside the shape and relationships) to describe the data and its correctness.

GraphQL has a JSON-like syntax.

GraphQL can perform read and write operations. Queries are read operations to fetch data from the server. Mutations are write operations to modify the data on the server. If an operation doesn't include a type, GraphQL will treat it as a query.

Here's an example of a GraphQL query and a server response that corresponds to it. Notice the query and the response have the same shape/structure.

{
  author {
    name
    age
    posts {
      title
      published_at
    }
  }
}
{
  "data": {
    "author": {
      "name": "Zaiste",
      "age": 17,
      "posts": [
        {
          "title": "GraphQL 101",
          "published_at": "2017-07-24 11:59:21"
        },
        {
          "title": "Immutable Infrastructure 101",
          "published_at": "2017-07-17 11:59:21"
        }
      ]
    }
  }
}

A GraphQL query can have a name. It is recommended to mark queries with the query keyword and name them so it is clear what they do. GraphQL queries can have comments.

query FetchAuthor {
  author {
    # Fetch only the author name
    name
  }
}

GraphQL query consists of fields, e.g. name or age from the previous example. Queries can accept arguments (which can be either required or optional).

{
  author(id: 9) {
    name
    age
  }
}

Queries can have variables to make the arguments dynamic. Variables must be prefixed with $ sign followed by their type. They can have default values. ! designates $authorId as required.

query FetchAuthor($authorId: Int! = 9) {
  author(id: $authorId) {
    name
    age
  }
}

Aliases allow to fetch data from the same field, but with different arguments by using custom names.

{
  bunia: author(id: 1) {
    name
	  age
  }
  zaiste: author(id: 9) {
    name
    age
  }
}

Fragments are named set of fields to avoid repetition.

{
  bunia: author(id: 1) {
    ...personal
  }
  zaiste: author(id: 9) {
    ...personal
  }
}

fragment personal on Author {
  name
  age
}

Directives in GraphQL provide a way to conditionally change the shape of queries. There are two directives available so far in GraphQL specification. Both directives accept a Boolean argument.

  • @include -> it conditionally includes a field or a fragment
  • @skip -> it conditionally skips a field or a fragment
query FetchAuthor($authorId: Int!, $isPrivate: Boolean!, $hasPosts: Post) {
  author(id: $authorID) {
    name
    age @skip(if: $isPrivate)
    posts @include(if: $hasPosts) {
      title
    }
  }
}

Mutations in GraphQL allow to modify the data on the server (write operations). Once issued, the server responds with the updated data.

mutation UpdateAuthorAge($authorId: Int!, $age: Int!) {
  updateAuthor(id: $authorId, age: $age) {
    age
  }
}

Mutations are executed serially in order to ensure data integrity, whereas queries are executed in parallel.

Schemas describe the data: what fields are available, how they are structured (their shape and relationships) and what are their types. Types allow the GraphQL server to determine whether a query is valid or not at runtime.

GraphQL comes with the following scalar types:

  • Int: a signed 32‐bit integer
  • Float: a signed double-precision floating-point value
  • String: A UTF‐8 character sequence
  • Boolean: true or false
  • ID: represents a unique identifier

Fields defined as one of the scalar types cannot have fields of their own. You can define custom scalar types using the scalar keyword.

Enumeration types or Enums are scalar types that represent a set of allowed values. They are defined using enum keyword.

enum Topic {
  JavaScript
  Clojure
}

Schemas are constructed using GraphQL schema language.

type Author {
  name: String!
  age: Int
  posts: [Post]
}