Welcome to new things

[Technical] [Electronic work] [Gadget] [Game] memo writing

GraphQL Usage Notes

The API I wanted to use was written in GraphQL. Since this was my first time using GraphQL, I thought I would make a brief note on how to use GraphQL from the client side.

What is GraphQL?

GraphQL is an API specification developed by Facebook.

Compared to the REST API, it has the advantage of reducing the number of API calls and the amount of data received.

For example, when acquiring a user name and a list of friends of the user, the REST API takes time to acquire the data due to the following reasons.

  • If user information acquisition and friend list acquisition were separate endpoints, two API calls would be made.
  • Even if only the user name is used, other information, such as email, is retrieved.

When this is done with GraphQL, the minimum necessary data can be obtained by sending the following request once to the API.

query {
    user(id:123){
        name
        friends{
            name
        }
    }
}

Try it out for yourself

The tutorial in the official documentation explains this with Star Wars data, so we tried it with a GraphQL wrapper for the Star Wars API as well.

Note, however, that the schema is different from the example in the official documentation.

To find out what kind of schema (data structure) is used, click on "Docs" in the upper right corner.

Basic Concepts of GraphQL

server

In GraphQL, types are first defined on the server.

The client then specifies the type and fields of data it wishes to retrieve.

For example, the Star Wars API server defines the following types. (partially modified for ease of explanation)

type Root {
    person(id: ID): Person
}

type Person {
    id: ID
    name: String
    birthYear: String
    gender: String
    filmConnection: PersonFilmsConnection
}

type PersonFilmsConnection {
    films: [Film]
}

type Film {
    title: String
    director: String
    producers: [String]
}

explanation

  • Root is the root type, from which queries to the server start (the type name can be defined arbitrarily on the server side, so the name will change depending on the GraphQL server being accessed).
  • Root has a function person, which returns the actor with the given id (Person)
  • Person has fields id, name, birthYear, etc.
  • The filmConnection field of Person is of type PersonFilemsConnection and is an array of movies (Film) in which Person appeared
  • Film has the fields title, director, etc.

client

From the client, starting from the root type, the client describes the data to be retrieved according to the structure of the type, and requests it to the GraphQL server.

For example, the id of Luke Skywalker is cGVvcGxlOjE=, and the following query is used to obtain the name and gender of Luke Skywalker.

query

query {
  person(id: "cGVvcGxlOjE="){
    name
    gender
  }
}

result

{
  "data": {
    "person": {
      "name": "Luke Skywalker",
      "gender": "male"
    }
  }
}

explanation

  • Under query, describe the data to be acquired, starting from the root type.
  • Function arguments are passed as "\:\".
  • The result will be returned under data with the structure specified in the query

Array elements can also be retrieved. In this case, the array is accessed with {}, which is the same as the element, instead of [].

For example, to retrieve a list of movie names in which Luke Skywalker appears, the query would be as follows

query

query {
  person(id: "cGVvcGxlOjE="){
    filmConnection{
      films{
        title
      }
    }
  }
}

result

{
  "data": {
    "person": {
      "filmConnection": {
        "films": [
          {
            "title": "A New Hope"
          },
          {
            "title": "The Empire Strikes Back"
          },
          {
            "title": "Return of the Jedi"
          },
          {
            "title": "Revenge of the Sith"
          }
        ]
      }
    }
  }
}

These are the basics.

If you think of the server as a type and the client as tracing the type, it is easy to imagine.

alias

Alias names can be attached to fields/functions.

query

query {
  hero: person(id: "cGVvcGxlOjE="){
    name
  }
  heroin: person(id: "cGVvcGxlOjU="){
    name
  }
}

result

{
  "data": {
    "hero": {
      "name": "Luke Skywalker"
    },
    "heroin": {
      "name": "Leia Organa"
    }
  }
}

variable

You can define variables and pass values to the query as variables.

This allows for convenient templating of queries.

query

query ($personID: ID="cGVvcGxlOjE="){
  person(id: $personID){
    name
  }
}

variable

{
  "personID": "cGVvcGxlOjU="
}

result

{
  "data": {
    "person": {
      "name": "Leia Organa"
    }
  }
}
  • The server will do the value expansion for you!
  • To set a default value, use "$\: \=\".
  • The type name should match the one set on the server side

Adding and updating data

Data is added and updated not in query but in a separate route called mutation.

query

mutation {
    createUser(name: "test test"){
        id
        name
    }
}

result

{
  "data": {
    "createUser": {
      "id": 123,
      "name": "test test"
    }
  }
}

usher who calls the names of wrestlers, sweeps the ring, etc.

The actual call is made by POSTing JSON with data set to the endpoint.

For JSON, set query to the query string and variables to the variable JSON.

(Example) JavaScript

import axios from 'axios';

(async () => {
    const query = `
        query ($personID: ID){
            person(id: $personID){
                name
            }
        }`;

    const variables = {
        personID: "cGVvcGxlOjE="
    };

    const json = {
        query,
        variables
    };

    const res = await axios.request({
        method: 'POST',
        url: 'https://swapi.apis.guru',
        headers: {
            'Content-Type': 'application/json'
        },
        data: json
    });

    console.log(res.data);

})();

Other

Certification

The authentication method is not determined by GraphQL and is left to the server implementation.

paging

Paging is not determined by GraphQL and is left to the server implementation.

For example, in the Star Wars API, when retrieving a list of actors, paging information is also output in the results, so the specification is to use that to specify the page and query.

query

query {
  allPeople(first: 2, after:"YXJyYXljb25uZWN0aW9uOjE="){
    pageInfo{
      endCursor
    }
    people{
      name
    }
  }
}

result

{
  "data": {
    "allPeople": {
      "pageInfo": {
        "endCursor": "YXJyYXljb25uZWN0aW9uOjM="
      },
      "people": [
        {
          "name": "R2-D2"
        },
        {
          "name": "Darth Vader"
        }
      ]
    }
  }
}

Impressions, etc.

When an application such as SPA fetches data from the server, GraphQL requires only one call, and the amount of data transfer is reduced, resulting in faster user response.

It is exactly the kind of thing Facebook developed out of necessity.

The user side is easy, but the server side is cumbersome to implement, although a library is available.

While the benefits are great for UX-oriented apps, I thought that for information provision APIs that do not care about response speed, the implementation would be too cumbersome and not worth the benefits.

Is that the reason why GraphQL has not caught on?

Reference Articles

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com

www.ekwbtblog.com