(Updated: )
/ #graphql #tooling #web development 

A gentle introduction to GraphQL API integrations

GraphQL is a great alternative to REST (or other HTTP API designs). This is an quick introduction to the core concepts around consuming a GraphQL API.

To see some examples consuming a GraphQL API:

Table of contents:

Table of Contents

What is GraphQL and what problems does it solve?

GraphQL is “a query language for your API”.

In plain English, it makes the client define what (nested) data it needs.

If we compare it to REST approaches:

  • the “pure” REST approach is to return IDs (or resource links) for any associations (or nested resources).
  • The less pure approach is to expand all the nested stuff.

The first situation leads to having to make lots of calls to fetch all the data, the second leads to huge payloads and slow load times.

In GraphQL, the client states in the request what it wants expanded, renamed or whatever else in the response.

It has some nice side-effects, eg. less need to version your API since the client defines what it wants and GraphQL has a way to deprecate fields.

Schema

GraphiQL, “An in-browser IDE for exploring GraphQL.” is available by navigating to the endpoint in your browser. It’s possible to generate the schema using the GraphQL CLI (requires Node + npm 5+):

npx graphql-cli get-schema --endpoint $BASE_URL/api/graphql --no-all -o schema.graphql

Queries

GraphQL query concepts

Fields

What we would like returned in the query, see the GraphQL documentation for “fields”. The GraphQL query for that returns the fields name, fleeRate, maxCP, maxHP, is the following (see output here):

{
  pokemon(name: "Pikachu") {
    name
    fleeRate
    maxCP
    maxHP
  }
}

Arguments

How we are going to filter the query data down, see the GraphQL documentation for “arguments”. To get the names of the first 10 pokemon we use pokemons (first: 10) { FIELDS }see the output here:

{
  pokemons (first: 10) {
    name
    fleeRate
    maxCP
    maxHP
  }
}

Aliases

Aliases give us the ability to rename fields, see the GraphQL documentation for “aliases”. We’re actually going to use it to map fields in the query eg. from camel to snake case:

{
  pokemon(name: "Pikachu") {
    evolution_requirements: evolutionRequirements {
      amount
      name
    }
  }
}

Running this query (here) gives us the following, where the evolutionRequirements is what we’ve aliased it to.

{
  "data": {
    "pokemon": {
      "evolution_requirements": {
        "amount": 50,
        "name": "Pikachu candies"
      }
    }
  }
}

Fragments

The definition of fields to be expanded on a type. It’s a way to keep the queries DRY and in general split out field definitions that are repeated, re-used or deeply nested, see the GraphQL documentation for fragments. It’s going to mean that instead of doing (see the query in action here):

{
  pokemon(name: "Pikachu") {
    weight {
      minimum
      maximum
    }
    height {
      minimum
      maximum
    }
  }
}

We can for example run this (query here):

{
  pokemon(name: "Pikachu") {
    weight {...FullPokemonDimensions}
    height {...FullPokemonDimensions}
  }
}

fragment FullPokemonDimensions on PokemonDimension {
  minimum
  maximum
}

The output is equivalent:

{
  "data": {
    "pokemon": {
      "weight": {
        "minimum": "5.25kg",
        "maximum": "6.75kg"
      },
      "height": {
        "minimum": "0.35m",
        "maximum": "0.45m"
      }
    }
  }
}

Running a GraphQL query

A GraphQL query can be run over POST or GET, it consists of:

POST (recommended)

  • Required headers: Content-Type: application/json
  • Required JSON body parameter: query: { # insert your query }

Raw HTTP request

POST / HTTP/1.1
Host: graphql-pokemon.now.sh
Content-Type: application/json

{
        "query": "{ pokemons(first: 10) { name } }"
}

cURL

curl -X POST \
  https://graphql-pokemon.now.sh/ \
  -H 'Content-Type: application/json' \
  -d '{
        "query": "{ pokemons(first: 10) { name } }"
    }'

GET

  • Required query param: query

raw HTTP request

GET /?query={%20pokemons(first:%2010)%20{%20name%20}%20} HTTP/1.1
Host: graphql-pokemon.now.sh

cURL

curl -X GET 'https://graphql-pokemon.now.sh/?query={%20pokemons%28first:%2010%29%20{%20name%20}%20}'

Top-level queries

There are 2 types of queries on the GraphQL Pokemon API at the moment:

  • First X pokemon: get all items (with whatever fields are defined in the query)
  • Single Pokemon by name: get a single item by its slug (with whatever fields are defined in the query)
  • Single Pokemon by id: get a single item by its slug (with whatever fields are defined in the query)

First X Pokemon

Queries of the form (see it in action in GraphiQL):

{
  pokemons(first: 5) {
    name
    # other fields
  }
}

Single Pokemon by name

Queries of the form (see it in action in GraphiQL):

{
  pokemon(name: "Pikachu") {
    name
    classification
    # other fields
  }
}

Note the double quotes ("") around the argument value

Single Pokemon by id

Queries of the form (see it in action in GraphiQL):

{
  pokemon(id: "UG9rZW1vbjowMjU=") {
    name
    classification
    # other fields
  }
}

Note the double quotes ("") around the argument value

Sample queries

Get some Pokemon to create strengths/weakness/resistance classification

Query (see it in GraphiQL):

{
  pokemons(first: 100) {
    name
    image
    maxHP
    types
    weaknesses
    resistant
  }
}

Get Pokemon and evolutions expanded for physical stats and attacks

Query (see it in GraphiQL):

{
  pokemon(name: "Pikachu") {
    ...PokemonWithAttack
    ...FullPhysicalStats
    evolutions {
      ...FullPhysicalStats
      ...PokemonWithAttack
    }
  }
}

fragment PokemonWithAttack on Pokemon {
  name
  attacks {
    fast {
      name
      type
      damage
    }
    special {
      name
      type
      damage
    }
  }
}

fragment FullPhysicalStats on Pokemon {
  height { ...FullDimension }
  weight { ...FullDimension }
}

fragment FullDimension on PokemonDimension {
  minimum
  maximum
}

Get selected Pokemon as named fields with their evolution names

Query (see it in GraphiQL).

We can rename top-level queries using aliases, that’s helpful if we want to do the following:

{
  pikachu: pokemon(name: "Pikachu") {
    ...FullPokemon
    evolutions {
      ...FullPokemon
    }
  }
  bulbasaur:pokemon(name: "Bulbasaur") {
    ...FullPokemon
    evolutions {
      ...FullPokemon
    }
  }
}

fragment FullPokemon on Pokemon {
  name
}

If you want to learn how to integrate with a GraphQL API:

unsplash-logoAnnie Spratt

Author

Hugo Di Francesco

Co-author of "Professional JavaScript", "Front-End Development Projects with Vue.js" with Packt, "The Jest Handbook" (self-published). Hugo runs the Code with Hugo website helping over 100,000 developers every month and holds an MEng in Mathematical Computation from University College London (UCL). He has used JavaScript extensively to create scalable and performant platforms at companies such as Canon, Elsevier and (currently) Eurostar.

Get The Jest Handbook (100 pages)

Take your JavaScript testing to the next level by learning the ins and outs of Jest, the top JavaScript testing library.