【Nuxt.js】ポケモンで学ぶGraphQL

[GraphQL Pokemon](https://github.com/lucasbento/graphql-pokemon)を使ってGraphQLを学んでいきます。

# GraphQLって？
GraphQLはFacebookがリリースした、Web API用のクエリ言語です。

REST APIと比べ
・エンドポイント(/user、/post/:id、等)が一つで済む
・クライアント側で取得するデータを選択できる
・リクエスト・レスポンスの型の定義ができる
などのメリットがある。

# 試しにGraphQLを使ってみよう
[GraphQL Pokemonコンソール](https://graphql-pokemon2.vercel.app)からデータを取得するために**クエリ**を送信します。

>   **GraphQLには三種類のクエリが存在する**
・query(データ取得) GET
・mutation(データ更新) POST/PUT/DELETE...etc
・subscription(サーバーサイドからのイベント通知) Websocket


例えばピカチュウの名前を取得するならqueryを使用します。

```:クエリ
query {
  pokemon(name: "pikachu") {
    name
  }
}
```

このクエリを[GraphQL Pokemonコンソール](https://graphql-pokemon2.vercel.app)にコピペして[▶]を押すと・・・

![demo](https://gyazo.com/a9bf7ff8902597e92cce3e73620b0316/raw)

このような画面になったでしょうか。

右側にはクエリに対するレスポンスが表示され、ピカチュウの名前だけ取得できていることがわかります。

***
***

では、GraphQLをプロジェクトに組み込んでみます。

# Nuxtプロジェクト作成
個人的な理由でNuxt.jsでGraphQLを使っていきます。

```:ターミナル
$ npx create-nuxt-app poke-gql

create-nuxt-app v3.4.0
✨  Generating Nuxt.js project in poke-gql
? Project name: poke-gql
? Programming language: JavaScript
? Package manager: Yarn
? UI framework: None
? Nuxt.js modules: (Press <space> to select, <a> to toggle all, <i> to invert se
lection)
? Linting tools: ESLint, Prettier
? Testing framework: None
? Rendering mode: Single Page App
? Deployment target: Server (Node.js hosting)
? Development tools: (Press <space> to select, <a> to toggle all, <i> to invert 
selection)
? Continuous integration: None
? Version control system: Git
```
# Apollo Client

クライアントサイドでGraphQLを使用するために[Apollo Client](https://www.apollographql.com/docs/react/)というライブラリが必要です。

Nuxt.jsにはApollo Clientをもっと使いやすくした[@nuxtjs/apollo](https://github.com/nuxt-community/apollo-module)というモジュールがあるのでこちらをインストールします。

```:ターミナル
$ yarn add @nuxtjs/apollo
```



***
イントスールが終わったらnuxt.config.jsに設定を追加します。

`"~/plugins/apollo-auth.js"`にclientOptionsの定義を記述します。
手動で作成しましょう。

```javascript:nuxt.config.js
modules: ['@nuxtjs/apollo'],
  apollo: {
    clientConfigs: {
      default: '~/plugins/apollo-auth.js',
    },
  },
```
// コピペ用
**touch plugins/apollo-auth.js**

```javascript:~/plugins/apollo-auth.js
export default () => {
  return {
    httpEndpoint: 'https://graphql-pokemon2.vercel.app/',
  }
}
```

`httpEndpoint: 'https://graphql-pokemon2.vercel.app/'`
このURLはGraphQL PokemonコンソールのURLです。

# クエリを記述する
クエリは`~/apollo/queries/getPokemons.gql`内に記述します。

**mkdir apollo**
**mkdir apollo/queries**
**touch apollo/queries/getPokemons.gql**


```:~/apollo/queries/getPokemons.gql
query GetPokes{
  pokemons(first: 151){
    number
    image
  }
}
```

`query`の隣の`GetPokes`とは何でしょうか？
クエリには**Operation type**と**Operation name**を指定できます。

>- Operation type
 - 実行する操作の種類を示す(query、mutation、subscription)
 - 基本必須です。
- Operation name
 - そのクエリが実行する操作を明示する名前
 - 任意ですが、デバッグやサーバーサイドでのログ収集に便利な為使用が推奨されています。

***

今回のクエリは`pokemon`から`pokemons`に変わっています。
パラメータも`name`から`first`に変わり数値を受け取るようになっています。

どのクエリが使用できるかは[GraphiQL](https://graphql-pokemon2.vercel.app)の画面右側の**Doc(Documentation Explorer)**から確認できます。

![スクリーンショット 2021-03-17 4.56.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635852/56c95e7d-790a-f561-4230-6857952a6725.png)

![スクリーンショット 2021-03-17 4.57.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635852/e8255fc2-ef36-614a-f5a7-a76d90786da5.png)

![スクリーンショット 2021-03-17 5.12.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635852/438886c9-0335-b0e0-34b4-1868047b22a0.png)

***

このようなフィールドと、対応した型を入れてくださいね〜といった仕様の記述のことを**スキーマ**と言います。

>**スキーマ**
GraphQL APIの仕様の定義
この定義に従ってクエリを実行することでデータを取得できる

***
では、レスポンスをテンプレートに表示させましょう。

```vuejs:index.js
<template>
  <div>
    <div v-for="poke in pokemons" :key="poke.id">
      {{ poke.number }}
      <img width="100" height="100" :src="poke.image" />
    </div>
  </div>
</template>

<script>
import getPokes from '~/apollo/queries/getPokemons.gql' // ・・・①

export default {
  data() {
    return {
      pokemons: [], // ・・・③
    }
  },
  apollo: {
    pokemons: {
      query: getPokes, // ・・・②
    },
  },
}
</script>
```

>①・・・記述したクエリをインポートします
②・・・apolloオブジェクトを追加し、その中にプロパティを定義
③・・・dataプロパティに初期値をセットします。初期値の定義はクエリのフィールドと一致している必要があります。

***

さて、今回のクエリは指定した数のポケモンと、その数分のポケモンのデータを取得するものです。
データにはnumberとimageを指定しました。

表示を確認します。

![スクリーンショット 2021-03-17 5.54.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/635852/5aaec609-bcc6-f20f-428d-0af5b9222da6.png)

ポケモン達が表示されています！

みんなもポケモンゲットだぜ！！！

# おまけ

### 変数を使用する
apolloオブジェクト内のvariablesプロパティでクエリに変数を渡せます。

```javascript
apollo: {
    pokemons: {
      query: getPokes,
      variables: {
        num: 1,
      },
    },
  },
```
```:~/apollo/queries/getPokemons.gql
query GetPokes($num: Int!){
  pokemons(first: $num){
    number
    image
  }
}
```
***

### 変数をリアクティブにする

variablesを関数にすることでリアクティブになる

```javascript
apollo: {
    pokemons: {
      query: getPokes,
      variables() {
        return {
          num: this.num,
        }
      },
    },
  },
```

![demo](https://gyazo.com/442c1c7d5444e1337df00e113dbcb278/raw)

***

### this.$apollo

今までapolloオブジェクトでapollo-clientインスタンスにアクセスしていました。
この方法を**Smart Query**と言います。

今度はthis.$apolloを使用してapollo-clientインスタンスにアクセスしてみます。

```javascript
async created() {
    this.pokemons = await this.$apollo.query({
      query: getPokes2,
      variables: {
        num: this.num,
      },
    })
  },
```

`this.$apollo.query`はPromiseを返します

***



### ローディング
`this.$apollo.loading`でローディングを検知できます。

```html
<div v-if="$apollo.loading">ちょっと まってね</div>
```

![demo](https://gyazo.com/aac0c053e6048f79af6dd0e27aacde83/raw)