Elixir × GraphQLシリーズ ③ ミューテーションとサブスクリプション設定の追加

# Elixir × GraphQLシリーズ  
## ~ Abshinthe と Abshinthe crient の 連携 ~

この記事は「[Elixir Advent Calendar 2022](https://qiita.com/advent-calendar/2022/elixir)」22日目の記事です
東京にいるけどfukuokaexのYOSUKEです。

このシリーズは３部作目です。
前回までは以下
* [Elixir × GraphQLシリーズ① ~ サクッとGraphQLサーバーのセットアップ ~ ](https://qiita.com/drafts/f50277343d6114f37d33)
* [Elixir × GraphQLシリーズ ② ~ サクッと Abshinthe-crient Setup ~](https://qiita.com/Yoosuke/private/f93204ce1c34684d48b9)

* 今回は、今まで簡単に説明していた？（いや、ほとんど説明はしてない）GraphQL Serverのセットアップは完了しているので、GraphQLの説明と、その利用方法を少し解説していきます。

## GraphQLとは
GraphQLとは、アプリケーション プログララミング インタフェース(API）の1つで、REST APIなどと違って、クエリを駆使して、1つのエンドポイントからデータを柔軟に利用したいものだけ取得する事ができる仕組みです。

詳しく知りたい方は、[GraphQLの学習](https://graphql.org/learn/)を参考にしてください。

## 今回の構成
* よくある、GraphQLを利用した開発の構成として使われるのは、フロント側の実装を（React,VueなどのSPA）で開発し、バックエンド側をGraphQLサーバーとして開発し、1つのエンドポイントで繋いで利用する。というものだと思いますが、

* 今回は、フロント側を LiveView を利用して、 フロントとバックを両方 Elixirで作成する方法を試していきます。
* LiveViewを活かしたいので、クエリだけでなく、ソケットを利用したステートフルな形を準備していきます。

## クエリとミューテーションとは
GraphQL の操作はクエリとミューテーションです。これらを create (作成)、read (読み取り)、update (更新)、delete (削除) (CRUD) モデルに照らして考えると、クエリは read (読み取り) に相当します。その他すべて (create、update、delete) はミューテーションによって処理されます。

クエリまでは、シリーズ①、②で実装してきましたが、ミューテーションの実装はまだでしたので、まずはミューテーションを実装していきたいと思います。

## GraphQL ミューテーションの実装

スキーマにミューテーションの追加

```schema.ex

defmodule OpenDataQlWeb.Schema.Schema do
  use Absinthe.Schema

# 省略 #

  mutation do
    field :create_place, :place do
      arg :lat, non_null(:float)
      arg :location, non_null(:string)
      arg :lon, non_null(:float)
      arg :name, non_null(:string)
      arg :scale, non_null(:integer)
      resolve &OpenDataQlWeb.Resolvers.Community.create_place/3
    end
  end

end

```

リゾルバの作成

```community.ex
defmodule OpenDataQlWeb.Resolvers.Community do
  alias OpenDataQl.Community
# 省略 #
  def create_place(_, params, _) do
    case Community.create_place(params) do
      {:error, _} -> {:error, "Could not create place"}
      {:ok, _} = success -> success
    end
  end
```

早速試してみましょう。
Firefox のADD ON にある GraphQL Clientからミューテーションを実行してみます。

![スクリーンショット 2022-12-12 12.05.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72962/09713ae4-3afc-e84e-a7fa-f768e57b0158.png)

今回は、変数を使わず、直接値を入力してミューテーションを実行してみました。


```graphql
mutation{
  createPlace(name: "name5", lat: 123.4, lon: 234.5, location: "name", scale: 9){
    name
	lat
    lon
    location
    scale
    id
  }
}
```

![スクリーンショット 2022-12-12 12.03.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72962/8f41990a-709a-68f9-3e61-f5cee0540013.png)

ちゃんと挿入されているか確認してみましょう。以下のクエリを実行して確認します。

```graphql
{
  places{
    name
    scale
    location
    lat
    lon
    id
  }
}
```

以下のように先ほどmutationで挿入したデータがちゃんと取得できることが確認できました。

![スクリーンショット 2022-12-12 12.09.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72962/bb86db8b-c77c-0392-890c-e26c5f9abcf9.png)


## GraphQL サブスプリクションの実装

GraphQL サブスクリプションは、サーバー内のイベントでデータをクライアントにリアルタイムでプッシュする方法になります。

クライアントは、特定のデータを要求するサブスクリプションを送信します。イベントが発生すると、そのイベントに対して実行され、結果のデータがプッシュされます。

そこで、ソケットを利用します。以下のコマンドでソケットを準びします。

### ソケットの追加

```zsh
$ mix phx.gen.socket User
```

2つファイルが生成されます。また、以下2つのファイルに関連付けを追記します。

```lib/open_data_ql_web/endpoint.ex
    socket "/socket", OpenDataQlWeb.UserSocket,
      websocket: true,
      longpoll: false
```

```assets/js/app.js
    import "./user_socket.js"
```

```user_socket.ex
defmodule OpenDataQlWeb.UserSocket do
  use Phoenix.Socket
  use Absinthe.Phoenix.Socket, schema: OpenDataQlWeb.Schema.Schema #<- 追記

```

### サブスクリプションの実装

Application 監視ツリーに追記

```application.ex
  def start(_type, _args) do
    children = [
# 省略 #
      {Phoenix.PubSub, [name: OpenDataQl.PubSub, adapter: Phoenix.PubSub.PG2]},
      {Absinthe.Subscription, OpenDataQlWeb.Endpoint}
    ]
```

Endpointに```Absinthe.Phoenix.Endpoint```を追加

```endpoint.ex
defmodule OpenDataQlWeb.Endpoint do
  use Phoenix.Endpoint, otp_app: :open_data_ql
  use Absinthe.Phoenix.Endpoint #<-追加
```

GraphiQL プラグを使用している場合は、 routerでsocketオプションを指定します。

```router.ex
  if Mix.env() == :dev do
    forward "/graphiql", Absinthe.Plug.GraphiQL,
      schema: OpenDataQlWeb.Schema.Schema,
      interface: :simple,
      socket: OpenDataQlWeb.UserSocket
  end
```

これで準びは完了です。

いよいよ、サブスクリプションの中身を実装して確認していきたいのですが、長くなりそうなので、サブスクリプションの実装編はまた時間ある時に何回かに分けて続きを書こうと思います。

今年は、サクッと書くのがコンセプトなので、（大作しすぎると続かなくなるので、今年はサクッと継続を重視していこうかと）