知識0のバカでも分かったgraphQL (Apollo, graphql-rails)

# 勉強したことのメモ

**[GraphQL](https://graphql.org)**
RESTでは一つのAPIアクセスに対して一つの決まった形のデータしか取ってくることができなかったが、graphQLを使うと自分の好きなデータを好きな組み合わせで取ってくることができる。そんな便利なGraphQLを使うには、Nuxt.jsでは**Apollo**、railsでは **graphql rails** というライブラリ(?)を使うことになる。

GraphQLの公式ドキュメントは英語だが結構わかりやすいので根本の概念はドキュメントを読むとなんとなく掴める。

##フロント側（今回はNuxt.js）
インストールは省略
`nuxt.config.js`に設定を書く。
内容は叩くAPIの場所

```nuxt.config.js
...,
apollo: {
    clientConfigs: {
      httpEndpoint: `http://localhost:4000/graphql`
    }
  },
...
```

### クエリ
クエリはデータくださいっていう要求のこと。
apollo/queries下に色々なクエリを書いていく
例えばapollo/queries/item.gql

```item.gql
# 引数の定義を書いておく
query Item($id: Int!){
  # 引数を書く
  item(id: $id) {
    #欲しいフィールドを書いていく
    id
    name
    price
    userId
    imageUrl
    explanation
  }
}
```
詳しくは[ドキュメント参照](https://graphql.org/learn/queries/)

### ミューテーション
ミューテーションはデータを変更したり削除したりする時に使う。
apollo/mutation下にmutationファイルを書いていく。
例えば、apollo/mutation/deletelike.gql

```deleteItem.gql
mutation ($id: ID!){
  deleteItem(input: {id: $id}){
    Item {
      id
    }
  }
}
```

### クエリやミューテーションの使い方

例として`index.vue`で使う場合を考える。

`script`タグ内で
`import (query/muationの名前) from　(gqlファイル名)`

例えば、

```index.vue
import Item from '~/apollo/querys/item.gql'
```
となる。

### クエリで取得したデータを直接`{{ item }}`とかで使いたいとき

`export default`内で

```index.vue
data(){
  ...
},
apollo {
  item: {
    query: Item
  }
},
...
```
と記述する。
そうするとtemplate内で変数itemが使えるようになる。

### メソッド内で使いたいとき
deleteItemを使うときは

```index.vue
method(){
  ...,
  hoge(){
    this.$apollo.mutate({
      mutation: deleteItem,
      #以下で定義する変数はmutationファイル内で定義した変数たち
      variables: {
        id: id
      }
    })
  }
  ...
}

```
こんな感じ。queryを使いたいときは`$apollo.query`にして`query: Item`にすれば良い。

#### おまけ
mutateした後は更新しないと見た目には反映されないので、

```
refetchQueries: [{
          query: getUsersGql
        }]
```
などを実行するとよい。

## サーバー側（今回はrails）

`Gemfile` に

```
gem 'graphiql-rails'
gem 'graphql'
```
と書いて`bundle install` した後に
`rails g graphql:install`すると、railsが自動で色々と設定してくれる。
`routes.rb`が以下のように更新されたり、

```routes.rb
...
if Rails.env.development?
  mount GraphiQL::Rails::Engine, at: '/graphiql', graphql_path: '/graphql'
end
...
```
`app`ディレクトリ下にgraphqlディレクトリが作成されたりする。

### Types
まず、graphQLが返すデータの型を定義する必要があり、これは
`app/graphql/types`
下に各モデルごとにファイルを作れば良い。
例としてuserモデルのtypeファイルを以下に示す。

```app/graphql/types/user_type.rb
Types::UserType = GraphQL::ObjectType.define do
  # 型名
  name 'User'
  # field :(要素名), (要素のタイプ(int, string, etc))
  field :name, !types.String
  field :email, !types.String
end
```

### クエリ
Typesファイルに定義された型を返すように要求するクエリを書いていく。

`app/graphql/types/query_type.rb`を作ってその中に全部のクエリを書く。

```app/graphql/types/query_type.rb
module Types
  class QueryType < Types::BaseObject
    # まずはクエリの大枠だけ書く
    # field :(クエリ名), Types::(定義した型名), null: (true/false) do
    field :user, Types::UserType, null: false do
      description 'ユーザ情報を1件取得する'  # クエリの説明
      argument :id, Int, required: true, description: 'ユーザID'  # クエリに回す引数
    end
    
    # 上のクエリの詳細の動きをクエリ名と同じ関数名で書く
    def user(id:)
      User.find(id)
    end
  end
end
```
### 実行される流れ
#### 
`/graphql`にアクセスすると`graphController`内のexecuteアクションが行われる

```routes.rb
Rails.application.routes.draw do
  ...
  post '/graphql', to: 'graphql#execute'
  ...
end
```
```graphqlController.rb
class GraphqlController < ApplicationController
  ...
  def execute
    variables = ensure_hash(params[:variables])
    query = params[:query]
    operation_name = params[:operationName]
    context = {}
    result = ApiSchema.execute(query, variables: variables, context: context, operation_name: operation_name)
    render json: result
  end
  ...
```
paramsにはquery呼び出し時のパラメータが格納されていて、指定されたクエリが呼び出されて実行される。