ゼロからGraphQL (ゼロ知識からDBなし最小構成でサーバを動かしてみる)

# GraphQLとは＝「データを柔軟に取り出せるAPI:bar_chart:」

大崎です。今回はできるだけ簡単にGraphQLを説明して試します。触ってみたいだけという方は[ライブデモ](https://graphqlbin.com/v2/xvmxf1)があります。



GraphQLは、**APIの形式**です。Facebookが開発、Mercariがマイクロサービス化するためにバックエンドのAPIとして採用しているそうです[^0]。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/96277/4fdbe73e-a6da-e222-0870-8da96578eade.png)


でも現在のデファクトのAPI形式はRESTです。なので、よくRESTと比べられます。

ですから先に結論を言っておくと、できることは**RESTと大きくは違いません**。:relaxed:
ただ、ちょっと**データ取得特化型の柔軟性（クエリが書けるという大きな特徴）**:thumbsup:を持っています。

### APIってそもそも

APIができることは、データを取ってくるだけでなく、以下のようにいろんな仕事ができるのですが、、、

- トランザクションを無事こなす:pencil2:
- 非同期処理をスタートする:timer:

ですが、今回は**データ取得:bar_chart:に話を絞ります**。
(GraphQLはデータ更新のしかけ`Mutation`も持っていますがあまり大事ではないので省略します)

### RESTの課題：デフォルトでは柔軟性がついてない

さて、ある目的のためにデータが欲しいとき、APIが提供してくれるデータでは過不足が生じます。

- 目的例:triangular_flag_on_post:
「近くのレストランの`名前`を評価順に`５個`取得する」

- 提供されているREST API `http://<URL>/restaurants/`
市内の**全レストラン**の一覧:worried:。
それぞれに`場所`と`名前`と`オーナー`と`電話番号`と・・・・・・と`従業員`を取得する:confounded:

５個だけ取るつもりが１０００個くらいデータが取れるし、無駄な値がくっついてくる。
時間もかかる。
欲しいものだけを取り出す、ということで思い出すのは下記例文にもあるSQLのようなクエリ言語ですが、残念ながらSQLはAPIとして提供されません。

```sql
select restaurant_name from all order by grade limit 5;
```

ODataというSQL over HTTP API形式がありましたが、「危険」という声がありました:u7981:[^1]。
SOAPで書けるかもしれません。でもXML書くのめんどくさい:thermometer_face:

### GraphQLの柔軟性＝「欲しいデータだけを要求できる:tools:」

GraphQLなら、柔軟性があるしデータ形式がRESTと同じJSONで使いやすいです。

RESTのPOSTでクエリを入力するやり方しかないかな、、、と思っていたところに出てきた**GraphQL**。
完璧とはいえないかもしれません。だってクエリの書き方がちょっと見慣れないですし。

```graphql
{
  restaurants(first: 5, sort: "grade") {
    name
  }
}
```

でも、このクエリをカスタマイズすると、**データの要求を柔軟に変更し、JSONでデータが取れます**。
柔軟性の部分は、詳しくは試しながら説明します。

返り値がJSONなので、Nodeなどで充実したいろんなツールのパワーをフル活用できます。
RESTからデータを取ってくるのと同じようにアプリが書けます！

# 最小構成でいいからまずはGraphQLを使う

### 最小セットのGraphQLサーバ＝「単なるWebサーバ（DBなし）」

今回はほぼ、素のGraphQLサーバです。

データは日本の市のリストを使います。といっても三鷹市と香取市の２つの市しか入っていません。

機能は一覧を取得する、という１個しかありません。
まだクエリらしいクエリは書けません。
でもまず動かします。

### サーバの機能は下記３つです。

- データを保持 (1)
- クエリを受け付け (2)
- クエリにあわせたデータを返す (3)

(1)は何もしなくていいです。いったん仮データをJSONで入れるだけ。
(2)は、`apollo-server`や`graphql-yoga`などのGraphQLのありがたいサーバが全部やってくれます
(3)が大事で、「どんなクエリにどんな値を返す」という設定を書くのが**resolver**です。


### 準備

今回はTypescriptでアプリを作ります。

環境に`npm`と`tsc`が入っていることを前提とします。

今回のGraphQLサーバアプリの名前を付けたフォルダを作ります。`npm init`でnode.jsの初期化をします。
`graphql-yoga`は今回使うGraphQLサーバライブラリです。
ほかにも`apollo-server`などがあります。
あとは３つくらい起動用のツールを入れます。

```sh
$ mkdir sample_app
$ cd sample_app
$ npm init
### 途中のmain指定では index.jsではなくてindex.tsを指定してください。
$ tsc --init
$ npm install graphql-yoga --save
$ npm install nodemon ts-node typescript --save-dev
```

`package.json`に`scripts`に`start`の行を追加します。`package.json`が以下のようになるといいです。

```package.json

{
  "name": "simple_json_graphql",
  "version": "0.0.1",
  "description": "Simple GraphQL Server from JSON data",
  "main": "index.ts",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "nodemon --ext ts,yaml --exec 'ts-node' index.ts"
  },
  "author": "Hiroyuki OSAKI (https://github.com/onelittlenightmusic)",
  "license": "ISC",
  "dependencies": {
    "graphql-yoga": "^1.16.2"
  },
  "devDependencies": {
    "nodemon": "^1.18.4",
    "ts-node": "^7.0.1",
    "typescript": "^3.1.1"
  }
}

```

### index.tsを作ります

さきほど説明した(1)から(3)を一行ずつ書いてみます。

```index.ts
import { GraphQLServer } from 'graphql-yoga'

// (1)データを保持
const locations = [
  {Japanese: "三鷹市",
  Prefecture: "Tokyo",
  Population: 180797},
  {Japanese: '香取市',
  Prefecture: 'Chiba',
  Population: 85193},
]

// (2)クエリを受け付け
const typeDefs = `
    type Location {
      Japanese: String
      Prefecture: String
      Population: Int
    }
    type Query {
      locations: [Location]
    }
`;

// (3)クエリにあわせたデータを返す
const resolvers = <any>{
  Query: {
    locations: () => locations,
  },
};

const server = new GraphQLServer({ typeDefs, resolvers })
server.start({port: 4040}, () =>
 console.log(`Your GraphQL server is running now ...`),
)
```

### 順に説明します

(1)は、べた書きでデータの配列を入れています。
だからDBは使っていません。
このデータはWikipedia[^2]からとってきたもので、PythonでスクレイピングしてJSON化したものです。[Pythonスクレイピングはこちらを参照](https://github.com/onelittlenightmusic/python_graphql_scraping_example/blob/master/scraping%20to%20GraphQL.ipynb)

```typescript

// (1)データを保持
const locations = [
  {Japanese: "三鷹市",
  Prefecture: "Tokyo",
  Population: 180797},
  {Japanese: '香取市',
  Prefecture: 'Chiba',
  Population: 85193},
]

```


(2)は、データの型`Location`とクエリ`locations`を作っています。`locations`クエリがリクエストされると配列`[Location]`が返るということを意味しています。
GraphQLの型とスキーマの詳しい説明は[本家ドキュメント](https://graphql.org/learn/schema/)がおすすめ。

```typescript

// (2)クエリを受け付け
const typeDefs = `
    type Location {
      Japanese: String
      Prefecture: String
      Population: Int
    }
    type Query {
      locations: [Location]
    }
`;

```

(3)は、クエリで返す値は、`locations`、つまり(1)の値全部ですよ、と言っています。
このように、GraphQLサーバ実装のメインの仕事は、**resolverに処理を書く**ことです。

```typescript

// (3)クエリにあわせたデータを返す
const resolvers = {
  Query: {
    locations: () => locations,
  },
};
```

### サーバを起動します

```sh

$ npm start
```

GraphQLサーバが`localhost:4040`で待機すると思うので、ブラウザでアクセスしてください。

`graphql-yoga`には、便利なクエリ実行GUIである`graphql playground`がついていて、デフォルトで起動することになっています。
その画面が見えるはずです。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/96277/96c69715-d026-8de1-62ec-2093903bb877.png)

### クエリを実行してみます

GraphQLのクエリはこんな風に書きます。
`graphql playground`の左側のクエリ編集ペインに打ち込みます。

```graphql

{
  locations {
    Japanese
    Prefecture
    Population
  }
}
```

打っていて気付いたと思いますが、**超強力な補完機能**がついています。構文ミスも型不整合もチェックしてくれます。

では、真ん中の:arrow_forward:ボタンを押してみます。

結果は出ましたか？

![image.png](https://qiita-image-store.s3.amazonaws.com/0/96277/d855bca9-21c5-856c-bd2d-6f30d4fce598.png)

(1)で入れたデータがそのままJSONで出てきましたね！

### 柔軟性（欲しいデータだけ要求する）を確認する :wrench:

ではクエリを変えてみます。
人口データなんかいらない、市の名前と県名だけでいい、という場合を想定します。

`#`を`Population`の前にいれてコメントアウトしてみます。

```graphql
{
  locations {
    Japanese
    Prefecture
    # Population
  }
}
```

実行してみます。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/96277/54aa28d8-4142-6bb5-bab9-ff3c44d4e220.png)

前のクエリでは人口データが入って返ってきていたのですが、今回は入っていません。
つまり、**要求した２つのフィールド`Japanese`(市の名前)と`Prefecture`(県名)だけが返ってきている** ことがわかると思います。

GUIからではなくそのままAPIとしても使えるので、このGUIで試しながら、APIを使うアプリを実装できます。

ちなみに、全部の日本の市が入ったGist上のデータ（自作）を参照するように改造すると[Githubレポジトリ](https://github.com/onelittlenightmusic/simple_json_graphql/blob/master/index.ts)のような形になります。
クエリも一部追加自作していますので良ければご覧ください。

## ライブデモ

Herokuに公開し、GraphQLPlaygroundをgraphqlbin上で公開しました。

日本の市を一覧化し、人口、設立日など統計値も加えました。

https://graphqlbin.com/v2/xvmxf1

![image.png](https://qiita-image-store.s3.amazonaws.com/0/96277/e3d5f1cf-7bcb-c8ca-00a3-997674afffcb.png)

実は`type`にコメントを書くと、自動補完のときや右の`schema`ペインを参照したときに型の情報も表示してくれるんですね！便利です。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/96277/7d636ee5-a444-b807-febf-bfe86c08c608.png)

総務省からは市区町村コードも公開されていますので、これも随時GraphQL化したいと思います。

[総務省/全国地方公共団体コード](http://www.soumu.go.jp/denshijiti/code.html)

### 他の参考文献

https://qiita.com/hassaku_63/items/d12d36d17cce536ade66
[Apollo-server版最小構成デモ](https://github.com/apollographql/graphql-server-example)

[^0]: [Mercari: Web Application as a Microservice](https://speakerdeck.com/mercari/web-application-as-a-microservice?slide=46)
[^1]: [GraphQL is not OData](https://jeffhandley.com/2018-09-13/graphql-is-not-odata)

[^2]: [List of cities in Japan/Wikipedia](https://en.wikipedia.org/wiki/List_of_cities_in_Japan)