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 functionperson
, which returns the actor with the given id (Person
)Person
has fieldsid
,name
,birthYear
, etc.- The
filmConnection
field ofPerson
is of typePersonFilemsConnection
and is an array of movies (Film
) in whichPerson
appeared Film
has the fieldstitle
,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
- https://qiita.com/NagaokaKenichi/items/f83148f4903b17d1d2f0
- https://qiita.com/bananaumai/items/3eb77a67102f53e8a1ad
- https://gift-tech.co.jp/articles/start-graphql/
- http://apis.guru/graphql-apis/