【GraphQL 入門】 Next.js製ブログで記事情報を取得できるAPIを実装するための勉強ノート

GraphQL入門したいのだけどなかなか理解できずに四苦八苦した記録。もしGraphQL入門でつまづく人がいたら参考になればいいと思う。対象は完全に0からプログラミングを初めて今まさに勉強中というレベル。基礎知識のない状態で参考記事を読んでいるので、理解できるまでの解釈を独り言のように記録しているので無駄が多いかもしれないけど、ご勘弁ください。

一応具体的な目標はあって、Next.jsでブログシステムを自作したくて、記事情報を取得する処理を実装する方法を勉強中。GraphQLなら、記事をmdファイルで作成してfrontbatterの記事情報を取得できると聞いて入門することに。だいたいのイメージは分かるのだけど、自作でカスタマイズして動作させるには知識が足りない。まずはGraphQLの処理をコピペでなく自作できるレベルを目指す。

↓初心者には怪文書でしかない。これをどうにかして読めるレベルになりたい。

>参考文献
>[Getting Started With GraphQL.js（公式ドキュメント）](https://graphql.org/graphql-js/)

#GraphQLとは

<img width="400" alt="スクリーンショット 2019-12-28 22.02.07.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278293/bc1c3ced-3d42-8887-f917-6cf18ee76a71.png">

とりあえず基本情報に触れておく。

>参考文献
>[The GitHub GraphQL API](https://github.blog/2016-09-14-the-github-graphql-api/)

GraphQLとは、2015年にFacebookが発表したAPIのクエリ言語であ、データ用に定義した型システムを使用してクエリを実行するためのサーバー側のランタイム。ウェブAPIの開発においてRESTやその他のWebサービスと比較して、効率的、堅牢、フレキシブルなアプローチを提供する。

GraphQLがどういう位置付けの技術なのかというと、[GitHubのエンジニアのブログ](https://github.blog/2016-09-14-the-github-graphql-api/)を読むと大体わかる。Node.jsなどを採用したWebアプリではサイト内のデータを扱うのにJSONを採用するのが一般的なイメージだけど、かつてはXMLが使われていたらしい（多分）。XMLはHTMLに似た構文で記述するものなのでコード量が半端なく多くて面倒で、しかも読みにくい。`< >`でタグを付けないといけないので煩わしいしタイピングも面倒い。なのでJSONの方が嬉しい、という挨拶に続けて、APIはRESTfulで設計されているが、RESTでは痒いところに手が届かない。一方でGraphQLはデータリクエストの柔軟性やデータ型チェックに長けているし、通信の無駄が解消される。みたいなことが書かれていた。

###REST に 比べて GraphQL のクエリ発行が効率的という話

>参考文献
>[GraphQL を RESTful API と比較しながら実装して理解する](https://tech.jxpress.net/entry/graphql-vs-rest)

クエリで指定されるフィールドごとに`resolver`という関数を定義し、対応するデータをDBなどのデータストア等から取得して返すようにする。

REST APIとの違い。例えば`/post/a`と`/post/b`と`/post/c`というリソースをリクエストしたいとする。REST APIではソース毎にHTTPリクエストを発行する必要がある。

1. get /post/a
2. get /post/b
3. get /post/c

しかし、GraphQLでは`/graphql`に問い合わせてJSONファイルとして一括でリクエストできる。

1. /graphql {a, b, c}

必要なデータだけを単純なクエリ発行で取得できるので効率的。

###気になった用語

**エンドポイント**
APIにアクセスするためのURIを指す。基本的にはURIがリソースを指し、URIとHTTPメソッドの組み合わせで処理の内容を表すのが良い設計であるとされている。REST APIではリソース毎にURLを指定してリクエストする必要があるが、GraphQL APIではエンドポイントが一つしかない上に一回のリクエストで複雑なデータ取得要求が可能。

**クエリ Query**
データの取得要求。GETメソッドのようなもの。

**ミューテーション Mutation**
データの更新要求。POSTメソッドのようなもの。

###`type Query` と `type Mutation` の違い

>参考文献
>[Apolloでの綺麗なAPI実装（GraphQL）を試す](https://qiita.com/teradonburi/items/2ad98c7c21f1f6cc4390)

データの取得にはQuery、データの更新にはMutationを使う。スキーマの定義の仕方は下記コード。

```server.js

const typeDefs = `
  type Query {
    フィールド名(引数): 返却データ型
  }
  type Mutation {
    フィールド名(引数): 返却データ型
  }
}
`

// resolvers
const resolvers = {
  Query: {
    フィールド名: (引数) => 返却データ,
  },
  Mutation: {
    フィールド名: (引数) => 返却データ,
  },
}
```

データの基本型。

`Int` 32bit整数型
`Float` 浮動小数型
`String` UTF-8 文字列型
`Boolean` true もしくは false
`ID` ユニークなスカラー値、キャッシュに使われる。Stringをシリアライズ（直列化）したデータで保存される。


`type Query`や`type Mutation`のブロック内にメソッドを追加。複数メソッドを定義できる。コメントアウトは`"""`で囲む。

```server.js
const typeDefs = `
  type Query { 
    books: [Book],
    items: [Item],
  }

  """
  返却するデータ構造
  """
  type Book { title: String, author: String }
  type Item { title: String }
`

// resolvers
const resolvers = {
  Query: {
    books: () => books,
    items: () => items,
  },
}
```

#とりあえずここまで情報収集したけどさっぱり理解できてない

ここまではGraphQLの雰囲気を掴んでおこうという趣旨。他にも公式ドキュメントや多くの記事を参考にしたけど、素人が理解できるほど優しい世界ではなかった。できればシンプルなコードで順を追ってコーディングしていけば動作するアプリが作れる日本語の教材が欲しいところ。いくらググって考えても理解できないから贅沢言うしかないのである。

#謎が解けていったときの勉強過程の覚書

しばらくはGraphQLに入門したくてもなかなか理解できずに入り口でウロウロさせられていた。一体何が悩みかと言うと、`Query`でデータ要求とか、`Mutation`でデータ更新とか、どこかにあるオブジェクトデータ群に対してリクエストするんだということは何となく分かっていたけど、GraphQLを介してリクエストを出すというイメージが全然浮かんでこなくて、結局アプリの中でGraphQLを起動するにはどうすんの、と思っていた。オブジェクトデータ群は外部ファイルやDBに書いておいてインポートしたりすればいいし、Queryの出し方も普通に途中にコードを書けばいいし、スキーマもそのそばに書けばいい。しかし、GraphQLの起動はどのコードが発端なんだ、Expressがないといけないのか、どうなんだ。

しばらくは下記の記事を参考に模索。`apollo-server-express`というモジュールを使ってるらしい。ググってると「apollo」という単語は何度も見かけたけど、これが必須なのかいわゆる便利ツールなのかはまだよく分からん。プレーンな状態でGraphQLを動かす方法とかはそのうち全貌が分かってくることに期待。

>参考文献
>[Apolloでの綺麗なAPI実装（GraphQL）を試す](https://qiita.com/teradonburi/items/2ad98c7c21f1f6cc4390)

この記事では以下のモジュールを使っている。

- apollo-server-express
- graphql-tools
- graphql
- express
- body-parser

```zsh
$ npm install --save apollo-server-express graphql-tools graphql express body-parser
```

この記事のコードは下記。コメントアウトのおかげでどの部分が何を意味するのかわかりやすかった。しかしGraphQLへの接続のあたりがやっぱりよく分からなかった。

```server.js
const express = require('express')
const bodyParser = require('body-parser')
const { graphqlExpress, graphiqlExpress } = require('apollo-server-express')
const { makeExecutableSchema } = require('graphql-tools')
const app = express()

process.on('uncaughtException', (err) => console.error(err))
process.on('unhandledRejection', (err) => console.error(err))

app.use(bodyParser.urlencoded({extended: true}))
app.use(bodyParser.json())


// GraphQLスキーマ定義
const typeDefs = `
  """
  type Query　（必須）
  """
  type Query { books: [Book] }

  """
  返却するデータ構造
  """
  type Book { title: String, author: String }
`

// ダミーデータ
const books = [
  {
    title: 'Harry Potter and the Sorcerer\'s stone',
    author: 'J.K. Rowling',
  },
  {
    title: 'Jurassic Park',
    author: 'Michael Crichton',
  },
]

// resolvers
const resolvers = {
  Query: { books: () => books },
}

// スキーマ生成
const schema = makeExecutableSchema({
  typeDefs,
  resolvers,
})

// GraphQLエンドポイント
app.use('/graphql', bodyParser.json(), graphqlExpress({ schema }))

// GraphiQL：GraphQLクエリのvisual editor
// TODO: 本番デプロイ時はアクセス出来ないようにする
app.use('/graphiql', graphiqlExpress({ endpointURL: '/graphql' }))

app.listen(5000, () => {
  console.log('Access to http://localhost:5000')
})
```

###STEP 1　もう少しシンプルなサンプルコードで勉強したいと思った

このコードを解読しようといろいろ調べたけど決定打はなかった。そこでapolloとexpressに絞ってググるとこの記事に行き着いた。以降、この2つの記事のコードを比較しながら勉強していく。

>参考文献
>[Apollo+Expressで始めるGraphQL超入門 ~ GraphQLをざっくり理解する](https://qiita.com/zonomaa/items/5de4b14dcd839db5f148)

この記事で使うモジュールは以下。さきほどの記事よりインストールする数が少なくてうれしい。

- express
- apollo-server-express
- graphql

```zsh
$ $ npm i --save express apollo-server-express graphql
```

この記事のコードを参考にする。

```app.js
const express = require('express');
const { ApolloServer, gql } = require('apollo-server-express');

// Construct a schema, using GraphQL schema language
const typeDefs = gql`
  type Query {
    hello: String
  }
`;

// Provide resolver functions for your schema fields
const resolvers = {
  Query: {
    hello: () => 'Hello world!',
  },
};

const server = new ApolloServer({ typeDefs, resolvers });

const app = express();
server.applyMiddleware({ app });

app.listen({ port: 4000 }, () =>
  console.log(`🚀 Server ready at http://localhost:4000${server.graphqlPath}`)
);
```

とりあえずコピペしてサーバー起動すれば動く状態ではある。

```zsh
$ node app.js
```

そしてここであることに気づく。

###STEP 2　エンドポイント`/graphql`ってどゆこと？

REST APIはリソースをリクエストするたびにそのURL（エンドポイント）にアクセスする必要があるというのはどこを見ても書かれていることだけど、GraphQLのエンドポイントは`/graphql`の一つしかないという意味がよく分からなかった。でもテストアプリを走らせて`http://localhost:4000/graphql`にアクセスするとGraphQLのビジュアルエディタが開かれた。なるほど、GraphQLのAPIが動作するURLが`/graphql`で、リクエストをここのアプリで捌いていっただけだったのか。プログラムとして動作するのは当たり前だけど、ビジュアルエディタとして手動で操作することもできるというわけだった。

<img width="732" alt="スクリーンショット 2019-12-29 22.11.16.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278293/37f5c238-b962-12b2-d0fd-751bbbbc439d.png">

###STE 3　ちなみにbody-parserについて

一つ目の参考記事と、二つ目の参考記事では、インストールするモジュールの数に差があった。一つ目の記事では以下のモジュールを多くインストールしていた。

- graphql-tools
- body-parser

一つ目の記事のサンプルコード中にある`bodyParser.json()`という記述は、Express4.xでは外部ミドルウェアだったものを標準搭載して`express.json()`で仕様できるらしい。ググっていろんなバージョンやいろんな方針でコーディングしている記事があるけど、一応どちらも使えるし同じ機能を実装しているということを把握しておきたい。ちなみに標準機能として実装しておけば`const bodyParser = require('body-parser')`を宣言する必要がなくなって効率的。

というわけで二つ目の記事のやり方の方が最新情報に近いと判断して、そっちを参考に勉強してくことに。

>参考文献
>[Body-ParserがExpressにexpress.json()として標準搭載されている話](https://qiita.com/atlansien/items/c587a0bf2f7f9022107c)

###STEP 4　ビジュアルエディタを使ってみる

いろいろスッキリしたところでGraphQlエディタを動かしてみた。`{hello}`を要求すると`Hello World!`が返ってきた。

<img width="690" alt="スクリーンショット 2019-12-29 22.50.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278293/001cfa75-b410-dffb-cfb0-7002dd802f6f.png">

このHello Worldは`app.js`で`resolvers`に書いておいたHello Worldが渡されてるだけ。試しに適当な名前のデータを要求したら「そんな名前のデータはねえ」と言われる。

<img width="691" alt="スクリーンショット 2019-12-29 22.50.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278293/f3066009-6afa-e70a-0604-d5a0763845c9.png">

ちなみにエディタの右端についている「SCHEMA」タブを開くと要求したデータの型を定義したスキーマを自動生成だか読み込みだかしてくれている。

<img width="1680" alt="スクリーンショット 2019-12-29 23.25.26.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278293/b9756d89-7149-f895-b2f8-de495b13df97.png">


###STEP 5　オブジェクトデータ群を用意する方法

ではGraphQLでデータ要求する相手となるオブジェクトデータ群はどこに存在するのかと言うと、DBを実装するのはまだ難しいので、外部ファイルにオブジェクトを記述しておいて`exports`すればいいとのこと。なるほどこれがデータ保管庫か。

とりあえず手本通りに`users`というオブジェクトを作り、`id` `name` `age` `created_date`という4つのkeyを定義した。このオブジェクトデータの構造は作りたいアプリによって自由に定義できる、と思う。

```users.js
exports.users = [
  {
    id: 1,
    name: 'Zonomaa',
    age: 25,
    created_date: '2019-04-05 01:35:25.000'
  },
  {
    id: 2,
    name: 'Masa',
    age: 28,
    created_date: '2019-04-10 08:23:51.000'
  },
  {
    id: 3,
    name: 'Ms',
    age: 30,
    created_date: '2019-04-10 08:32:09.000'
  }
  // ... 適当にいくつか作ってみましょう
];
```

メインの`app.js`のファイルの冒頭でrequireを追記し、オブジェクトデータ群を記述したファイルを読み込んでおく。メインの`app.js`でこのファイルを読み込んでおき、`app.js`の中でエンドポイントの`/graphql`にリクエストすればデータを引っ張ってこれるというわけか。


```app.js
const express = require('express');  // いつもの
const { ApolloServer, gql } = require('apollo-server-express');  // ここまだよく分からん
const { users } = require('./users');  // ここでオブジェクトデータ群を読み込み
```

へぇ、importじゃないんだ。importとrequireの使い分けまだよく分からん。Reactコンポーネントでよくimportを見かけるなぁ程度の知識しかない。クラスやメソッドはimportとexport？ そしてファイルやモジュールはrequireとexports？ あとで勉強しとこう。

>参考文献
>[Apollo+Expressで始めるGraphQL超入門 ~ GraphQLをざっくり理解する](https://qiita.com/sunadorinekop/items/c8e87bbea496a5351234)

###STE 6　スキーマを定義してデータ要求をカスタマイズ

データの用意の仕方、GraphQLの起動の仕方、この2つのイメージが掴めた。ここでGraphQLでどのようにデータを要求するかを考えていく。ここで要求するデータは先ほど作成した`users`なので、そのオブジェクトデータのvalueの型を定義していく。`app.js`の`typeDefs`を編集する（この時点ではhelloがはいっているだけなので）。

```app.js
// Construct a schema, using GraphQL schema language
const typeDefs = gql`
  type User {
    id: Int
    name: String
    age: Int
    created_date: String
  }
  type Query {
    users: [User]
  }
`;
```

型の定義は`type User{}`、データ要求は`type Query{}`に書かれているのが分かる（よく分からんが）。ええと、つまり`users.js`に盛り込んでいるオブジェクトデータの型に合わせて`typeDefs`の中で新たに`type User{}`というオブジェクトの型を定義することで`users.js`から要求通りのデータの受け渡し時の作法を決定した、ということっぽい。そしてデータを要求するには「さっき定義した型の`[User]`で頼んます」とやっているっぽい。

これで要求するデータの型の定義の`typeDefs`と、どのデータを要求するかの`Query`が完成した。これでアプリが動くと思いきや、動かないらしい。参考記事の著者も驚いて演出してくれている。

###STEP 7　`resolvers`でデータ取得のロジックを定義する

一つ目の参考記事の中でも`resolvers`とあって「これ何やねん」と悩んでいたけど、2つ目の参考記事でも登場した。どちらでも`resolvers`について触れているけど、1つ目の記事では具体的な仕様が、2つ目の記事では具体的なコーディングの順序が分かるような内容になっている。今はイメージが掴めてない状態なので2つ目の記事がありがたい。

GraphQLでは、データ取得のロジック（実際に処理を発火させる関数のこと？）は手動で記述するのがことになっているとのこと。普通は自動でやるものなのか？　とにかくここでやるべきは、`resolvers`の中の`Query`で`users`をreturnする関数書く。この`users`はさっき`type Query`で定義したやつ。

```app.js
const resolvers = {
  Query: {
    users: () => users
  },
};
```

つまり、`typeDefs`では`type User`でデータの型を定義し、`type Query`で要求するデータを指定する。そして、`resolvers`では`Query`でデータ取得の処理を記述する。これがGraphQLによるデータ要求の一連の流れになる。

###STEP 8　いろいろ自作したアプリでGraphQLの動作を確認する方法

ここでアプリ起動。

```zsh
$ node app.js
```

そして[http://localhost:4000/graphql](http://localhost:4000/graphql)にアクセスしてエディタを開く。そこでクエリを発行する。要は欲しいデータを書いて要求するということ。例えばこう。

```
{
  users {
    id
    name
  }
}
```

さっき定義してexportsしておいた`users`というデータ群から、`User`で定義したデータ型で、`id` `name` の2つのkeyのデータを要求する、という意味になる。

<img width="971" alt="スクリーンショット 2019-12-30 0.04.04.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278293/7c9624d3-6ef6-a07d-497d-3c6f5e80fe24.png">

動いた。お見事！！めっちゃスッキリした。
今回の勉強で分かったことはこんな感じ。

- インストールが必要なnodeモジュールの種類はとりあえず3つでいい
- GraphQLにアクセスする「エンドポイント」とはエディタが起動するURLだった
- オブジェクトデータ群は外部ファイルに書いてrequireしとけばGraphQLのエンドポイントからアクセスできる
- `typeDefs`の中で、データ型の`type objName`と、要求するデータの`type Query`を定義
- `resolvers`の中で、データ取得処理の`Query`を定義
- 今回はエディタでリクエスト出したけど実際のアプリではどうすんの？

#基礎をすっ飛ばして知識0からの勉強は大変すぎるという愚痴

こういう外部データの読み込みとか、APIへの接続とか、普通のエンジニアなら当たり前のように理解して、説明を省略した記事でも読み解くことができるのだろうけど、全くの素人からするとコードの1行1行の相関関係がわからないので理解に到ることのできる記事というのがなかなか見つからない。今回参考にした記事はとてもわかりやすくて助けられた。おかげでWebアプリでモジュールやらJSONデータを活用する作法というものが少し見えてきた。

今回学んだことを取り入れて自分が作りたいものに活かすにはどうすべきか。Next.jsでブログシステムを作ろうとしているが、記事情報の取得はGraphQLでどうにかできそうだ。記事はmdファイルで管理し、記事情報はfrontmatterに記述しているので、おそらく記事のmdファイルが入ったディレクトリ以下をrequireしておき、`/graphql`からアクセスして`frontmatter.id`とかで取得できる。うん、多分いける。

>次回
>[Gatsby.js（Next.js）のテーマ制作から学ぶ【React.js × GraphQL】のブログシステムでの投稿記事情報取得](https://qiita.com/oekaki-hoho-ron/items/43467bdcb9f85cfd53e7)