GraphQL(クエリ言語について)

# はじめに
GraphQLをざっくり理解するためにやったことをまとめときます。
GraphQLとはなんなのか的なことは、[こちら](https://employment.en-japan.com/engineerhub/entry/2018/12/26/103000)の記事に丸投げします。肩がねじ切れんばかりに投げてますが、自分はクエリ言語（フロント側、リクエスト）とスキーマ言語（サーバー側、レスポンス）に分けて写経などをしました。
RestAPIだと処理ごとにエンドポイントがありましたが、GraphQLではエンドポイントは１つです。GraphQLは、サーバー側で何かをする関数を用意しておき、その関数をフロント側で実行するイメージで理解しています。

今回はクエリ言語のみまとめます。

# クエリ言語
> クエリ言語は、GraphQL APIのリクエストのための言語で、これはさらにデータ取得系のquery、データ更新系のmutation、サーバーサイドからのイベントの通知であるsubscriptionの3種類があります。

はい、クエリ言語とはこういうことです。右肩が痛いですね。今回はsubscriptionについては触れませんが、ざっくりと説明するとsubscriptionは、サーバー側の変更をクライアント側でホットリロードするような時に使うみたいです。

query、mutation、paginationを[GraphQL Explorer](https://developer.github.com/v4/explorer/)（GithubのGraphQL API）で試していきます。paginationは、サーバーが一度に全てではなく、小さなチャンクに分けてデータを送信できる仕組みです。
[こちらの記事](https://www.robinwieruch.de/graphql-tutorial)を参考にしました。（GraphQLをReactで扱う方法もあり、分かりやすいです。）

## query

データの取得をする。(データの読み取り)
### 基本

```
// クエリ
query {
  viewer{ // GithubのGraphQL API
    name
    url
  }
}

// レスポンス
{
  "data": {
    "viewer": {
      "name": "kirisawa",
      "url": "https://github.com/kirikirisu"
    }
  }
}
```
・viewerをオブジェクトという。
・nameやurlをフィールドと言い、オブジェクトの特定のプロパティを要求するために使用される。
・クエリは単なるオブジェクトとフィールド、オブジェクトはフィールドとも呼ぶ。
・オブジェクトはサーバ側で"スキーマ"で定義されており、定義されているものをDocから確認できる。（後から出てくるMutationも）

### フィールド（オブジェクト）に引数を渡す

```
// クエリ
query {
  organization(login: "the-road-to-learn-react"){
    name
    url
  }
}

// レスポンス
{
  "data": {
    "organization": {
      "name": "The Road to learn React",
      "url": "https://github.com/the-road-to-learn-react"
    }
  }
}
```
・organizationフィールドに公開されいるorganization名を渡す。
・引数はstringだけでなく、numberやbooleanなどを渡す場合もある。
・フィールドレベルでリクエストを指定できるため、クエリの柔軟性が高い。

### ２つ以上の同一オブジェクトにデータを要求する
・２つ以上の同一のオブジェクトにデータを要求するには決まったエイリアスを使う。

```
// クエリ
// これは失敗する
query {
  organization(login: "the-road-to-learn-react") {
    name
    url
  }
  organization(login: "facebook") {
    name
    url
  }
}

// こうする
query {
  book:　organization(login: "the-road-to-learn-react") {
    name
    url
  }
  company:organization(login: "facebook") {
    name
    url
  }
}

// レスポンス
{
  "data": {
    "book": {
      "name": "The Road to learn React",
      "url": "https://github.com/the-road-to-learn-react"
    },
    "company": {
      "name": "Facebook",
      "url": "https://github.com/facebook"
    }
  }
}
```

### フィールドの共通化
・フラグメントを使用し、フィールドの再利用できる部分を抽出する。

```
// クエリ
query {
  book: organization(login: "the-road-to-learn-react") {
    ...sharedOrganizationFields
  }
  company: organization(login: "facebook") {
    ...sharedOrganizationFields
  }
}

fragment sharedOrganizationFields on Organization {
  name
  url
}

// レスポンス
{
  "data": {
    "book": {
      "name": "The Road to learn React",
      "url": "https://github.com/the-road-to-learn-react"
    },
    "company": {
      "name": "Facebook",
      "url": "https://github.com/facebook"
    }
  }
}
```
・「on Organization」は、GithubのGraphQL APIによって定義されたオブジェクトのタイプ。TypeScriptでの型定義のようなもの。

### 変数を使う
・"Query Variables"から変数を提供する。
・変数を使うことで動的にクエリを作ることができる。
・$ 記号で引数を変数として定義する。
・ ! はその引数が必須であることを示す。ない場合は必須ではない。

```
// クエリ
query ($organization: String!) {
  organization(login: $organization) {
    name
    url
  }
}

// Query Variables
{
  "organization": "the-road-to-learn-react"
}

// レスポンス
{
  "data": {
    "organization": {
      "name": "The Road to learn React",
      "url": "https://github.com/the-road-to-learn-react"
    }
  }
}
```

### クエリに名前をつける
・JavaScript無名関数と名前付き関数に似ている。
・付けた名前は、「オペレーションタイプ」や「オペレーションネーム」と言われる。

```
// 名前なし
query {
  organization(login: "the-road-to-learn-react") {
    name
    url
  }
}

// 名前あり
query OrganizationForLearningReact {
  organization(login: "the-road-to-learn-react") {
    name
    url
  }
}

// 名前あり、引数あり
query OrganizationForLearningReact($organization: String!) {
  organization(login: $organization) {
    name
    url
  }
}

// レスポンスは同じ
```

### ネストされたオブジェクトにアクセスする
・ネストされたオブジェクトにアクセスするためには、クエリもネストするだけ。

```
// クエリ
query OrganizationForLearningReact(
  $organization: String!,
  $repository: String!
) {
  organization(login: $organization) {
    name
    url
    repository(name: $repository) {
      name
    }
  }
}

// Query Variables
{
  "organization": "the-road-to-learn-react",
  "repository": "the-road-to-learn-react-chinese"
}
```

### ディレクティブ
・@include,@skipによって特定のフィールドを取得したり、取得しなかったりできる。
・@includeは、ifで評価する変数が true の時、フィールドを取得する。(falseの時取得しない)
・@skipは、falseの時フィールドを取得する。(trueの時取得しない)

```
// クエリ
query OrganizationForLearningReact(
  $organization: String!,
  $repository: String!,
  $withFork: Boolean!
) {
  organization(login: $organization) {
    name
    url
    repository(name: $repository) {
      name
      forkCount @include(if: $withFork)
    }
  }
}

// Query Variables
{
  "organization": "the-road-to-learn-react",
  "repository": "the-road-to-learn-react-chinese",
  "withFork": true
}
```

## mutation
GraphQL APIから提供され、何かを実行する。（データの書き込み）
### 例
・Githubのリポジトリにスターをつけるmutation。

```
// まずクエリによってリポジトリのidを取得する
query {
  organization(login: "the-road-to-learn-react") {
    name
    url
    repository(name: "the-road-to-learn-react") {
      id
      name
    }
  }
}
// レスポンス
{
  "data": {
    "organization": {
      "name": "The Road to learn React",
      "url": "https://github.com/the-road-to-learn-react",
      "repository": {
        "id": "MDEwOlJlcG9zaXRvcnk2MzM1MjkwNw==",
        "name": "the-road-to-learn-react"
      }
    }
  }
}

// そしてミューテーションによってリポジトリにスターをつける
mutation AddStar ($repositoryId: ID!) {
  addStar(input: { starrableId: $repositoryId }){
    starrable {
      id
      viewerHasStarred
    }
  }
}
// Query Variables
{
  "repositoryId": "MDEwOlJlcG9zaXRvcnk2MzM1MjkwNw=="
}

// レスポンス
{
  "data": {
    "addStar": {
      "starrable": {
        "id": "MDEwOlJlcG9zaXRvcnk2MzM1MjkwNw==",
        "viewerHasStarred": true
      }
    }
  }
}
```

・mutation(addStar)は、Github GraphQL APIから提供されている。
・上記は名前(AddStar)付きミューテーションで、名前は任意の名前をつける。
・クエリのようにオブジェクトとフィールドを使用して、ミューテーションの戻り値を指定できる。

```
// スターを消すミューテーション
mutation AddStar($repositoryId: ID!) {
  removeStar(input: { starrableId: $repositoryId }) {
    starrable {
      id
      viewerHasStarred
    }
  }
}

// レスポンス
{
  "data": {
    "removeStar": {
      "starrable": {
        "id": "MDEwOlJlcG9zaXRvcnk2MzM1MjkwNw==",
        "viewerHasStarred": false
      }
    }
  }
}
```

## Pagination
大きなデータを複数に分けて取得する

### 例
リポジトリリストの中からふたつずつリポジトリを取得

```
query OrganizationForLearningReact {
  organization(login: "the-road-to-learn-react") {
    name
    url
    repositories(first: 2) {
      edges {
        node {
          name
        }
        cursor
      }
    }
  }
}

// レスポンス
{
  "data": {
    "organization": {
      "name": "The Road to learn React",
      "url": "https://github.com/the-road-to-learn-react",
      "repositories": {
        "edges": [
          {
            "node": {
              "name": "the-road-to-learn-react"
            },
            "cursor": "Y3Vyc29yOnYyOpHOA8awSw=="
          },
          {
            "node": {
              "name": "hackernews-client"
            },
            "cursor": "Y3Vyc29yOnYyOpHOBGhimw=="
          }
        ]
      }
    }
  }
}
```

・ (first: 2)でリポジトリスト上から２つを指定。cursorは、そのリポジトリの位置情報。
・次に、取得した位置情報から２つのリポジトリを取得する。

```
query OrganizationForLearningReact {
  organization(login: "the-road-to-learn-react") {
    name
    url
    repositories(first: 2, after: "Y3Vyc29yOnYyOpHOA8awSw==") {
      edges {
        node {
          name
        }
        cursor
      }
    }
  }
}

// レスポンス
{
  "data": {
    "organization": {
      "name": "The Road to learn React",
      "url": "https://github.com/the-road-to-learn-react",
      "repositories": {
        "edges": [
          {
            "node": {
              "name": "hackernews-client"
            },
            "cursor": "Y3Vyc29yOnYyOpHOBGhimw=="
          },
          {
            "node": {
              "name": "react-local-storage"
            },
            "cursor": "Y3Vyc29yOnYyOpHOBUuT2A=="
          }
        ]
      }
    }
  }
}
```

## 最後に
最後まで読んでいただきありがとうございました。