GraphQL を試す


# 概要
フロントに React を使っているので、通信に Relay を使えないかと調査を始めましたが、API が REST ではなく GraphQL ということに気づき、まずは GraphQL の構文をいろいろ試してみることにしました。

Relay のチュートリアルで動作するサーバーに GraphQL を実行できる UI がついていたので、そこで試してみます。


# 公式チュートリアル

[公式のチュートリアル](https://facebook.github.io/relay/docs/tutorial.html) を一通り行うと、宝探しゲームが完成します。

これを使いながら GraphQL を学んでいきます。

# サーバーの起動

チュートリアル後のコードのイメージを[こちら](https://github.com/yasuhiro-okada-aktsk/sample_relay/tree/tutorial) (tag tutorial)にコミットしました。

起動は下記のように実行してください。

```bash
npm install
npm run update-schema
npm start
```
ブラウザから[http://localhost:3000](http://localhost:3000) をブラウザで開くとゲームができます。

# GraphQL のテスト用画面

GraphQL API は [http://localhost:8080](http://localhost:8080) で、ルートをブラウザで開くとテスト用の UI を表示できます。

<img width="817" alt="graphql-ui.png" src="https://qiita-image-store.s3.amazonaws.com/0/95713/35942a0b-e1b8-7693-7c65-07c41b22e9d7.png">

↑のような画面が表示されます。

「>」ボタンで実行、Docs には ```dada/schema.js``` で定義した schema が整形されて表示されます。
また、（分かりにくいですが、）左下の QUERY VARIABLES をクリックするとクエリーの変数を入力できます。

# GraphQL を試す

Docs をクリックすると、query と mutation の２つが出てきます。 query はデータの取得、 mutation はデータの更新をするときに使います。

game の取得を試します。
左の "Welcome ~" と書かれた左の領域に ```{game}``` と入力し、実行 (>) します。

右に結果が出ますが、今回は下のようにエラーが表示されます。

```json
{
  "errors": [
    {
      "message": "Field \"game\" of type \"Game\" must have a sub selection.",
      "locations": [
        {
          "line": 15,
          "column": 2
        }
      ]
    }
  ]
}
```

さらに入力した ```{game}``` も下のように変更されます。

```
{game {
  id
}}
```

これは game のどのフィールドを取得するか指定されていないため、エラーとなります。また、システムが理解できる範囲で入力が修正されます。
ここで再度実行すると、今度はクエリーの結果が表示されます。

```json
{
  "data": {
    "game": {
      "id": "R2FtZTox"
    }
  }
}
```

Docs -> Query -> Game （または```dada/schema.js```）を見ると、Game には id, hidingSpots, turnsRemaining の３つのフィールドがあることがわかります。

入力を次のように変えてみます。

```
{
  game {
    id
    turnsRemaining
  }
}
``` 

実行すると結果に turnsRemaining も含まれます。

さらに クエリーに ```hidingSpots``` を追加します。

```
{
  game {
    id
    turnsRemaining
    hidingSpots
  }
}
```

またエラーになり、クエリーが次のように修正されます。

```
{
  game {
    id
    turnsRemaining
    hidingSpots {
      edges {
        node {
          id
        }
      }
    }
  }
}
```

hidingSpots もオブジェクトのリストなので、どの情報を取得するのか指定する必要があります。

```
{
  game {
    id
    turnsRemaining
    hidingSpots {
      edges {
        node {
          id
          hasBeenChecked
          hasTreasure
        }
      }
    }
  }
}
```

宝探しの隠し場所（hidingSpot）の一覧が取得できました。

ここで一旦ゲーム画面（[http://localhost:3000](http://localhost:3000)）に戻ります。
※ ゲームで遊んでしまった人は、サーバーに状態が残っているので、サーバーを再起動してください。

ゲーム画面で適当なマスをクリックします。
もう一度上のクエリーを実行すると、更新したマスの情報が取得できます。

```json
            :
          {
            "node": {
              "id": "SGlkaW5nU3BvdDox",
              "hasBeenChecked": false,
              "hasTreasure": null
            }
          },
          {
            "node": {
              "id": "SGlkaW5nU3BvdDoy",
              "hasBeenChecked": true,
              "hasTreasure": false
            }
          },
          {
            "node": {
              "id": "SGlkaW5nU3BvdDoz",
              "hasBeenChecked": false,
              "hasTreasure": null
            }
          },
            :
```

次の更新クエリーで使用するので、```hasBeenChecked``` が ```false``` のノードの ```id``` の値をメモしておいてくください。（上の例なら```SGlkaW5nU3BvdDoz```など）

# 更新のクエリー

更新のクエリーですが、とりあえず実行してみます。
まず、次のように入力します。

```
mutation Mutation($input:CheckHidingSpotForTreasureInput!) {
  checkHidingSpotForTreasure(input:$input) {
    clientMutationId
  }
}
```

次に QUERY VARIABLES （入力欄が見えないときはクリックしてください）に次のように入力します。

```json
{
  "input":{
    "id":"SGlkaW5nU3BvdDoz",
    "clientMutationId":"0"
  }
}
```

```id``` の値は前にメモしておいた ```hidingSpot``` の ```id``` に変更してください。


ゲーム画面をリロードしたり、次のクエリーを実行すると更新されていることがわかります。

```
{
  game {
    id
    turnsRemaining
    hidingSpots {
      edges {
        node {
          id
          hasBeenChecked
          hasTreasure
        }
      }
    }
  }
}
```

更新クエリーの３行目の ```clientMutationId``` は Docs の ```CheckHidingSpotForTreasurePayload``` のフィールドで、データ取得のクエリーと同様に更新後に取得するフィールドを指定できます。

```
mutation Mutation($input:CheckHidingSpotForTreasureInput!) {
  checkHidingSpotForTreasure(input:$input) {
    clientMutationId
  }
}
```

QUERY VARIANTS に指定した値は、```CheckHidingSpotForTreasureInput```のフィールドです。

# 最後に
GraphQL に慣れるまではこの UI を使って出力や Docs を見ながらクエリーを組み立てると効率が良さそうです。

本当は Elixir / Phoenix でサーバーも実装してみたいのですが、まだライブラリがなく、自分で実装するには難しいのでもう少し力がついてからやってみようと思います。

Relay のチュートリアルが ES7 で書かれていました。近い将来 React も ES6 の class を使って実装することになりそうなので、 mixin など使えなくなる機能の代わりも調べる予定です。

# 参考
* [Tutorial | Relay Docs](https://facebook.github.io/relay/docs/tutorial.html)
* [graphql/README.md at master · facebook/graphql](https://github.com/facebook/graphql/blob/master/README.md)