GraphQL Meshを使ってMySQLのDBをGraphQLで操作してみる

# はじめに
GraphQL Meshとは複数のバックエンドサービスを束ねるGraphQLゲートウェイを構築するためのフレームワークです。GraphQL Mesh自体についての詳細は、数多くの記事が世の中にあるため省きます。

今回はGraphQL Meshを利用して、MySQLをラップしてGraphQLで扱う方法について試していきます。GraphQL Meshを経由して扱うことで、スキーマを自前で用意せずに既に存在するDB情報から自動で生成して取り扱うことができるようになります。

https://the-guild.dev/graphql/mesh/docs/handlers/mysql

# ローカルで動かすまでの手順

今回はローカルのdevサーバーとしてGraphQL Meshを立てて、任意のMySQLのDBを操作するところまで試します。

## 準備しておくもの
- ローカルからアクセス可能なMySQLサーバー
    - 検証用に適当なデータベースとテーブルを作っておく
    - 今回はMySQL8.0を使っています

## 必要なパッケージのインストール
適当なディレクトリを作成し、その中に必要なパッケージをインストールします。
```bash
$ yarn add graphql @graphql-mesh/cli @graphql-mesh/mysql
```

## .meshrc.ymlの作成
設定ファイルであるところの`.meshrc.yml`を先程作ったディレクトリに追加して、内容を記述します。
```yaml:.meshrc.yml
sources:
  - name: MySQL #任意のName
    handler:
      mysql:
        host: localhost #MySQLサーバーのホスト
        port: 3306 #ポート番号
        user: root #ユーザー名 今回は検証用にrootでアクセスする
        password: password #パスワード
        database: MyDatabase # 作成したDB名
```

`handler`を`mysql`とすることで、Meshが自動的にDBの定義を読み取り、DBの操作に必要なGraphQLのスキーマを用意してくれるようになります。この他にも、`host`や`port`等と並ぶところに`tables`や`tableFields`を配列で記述することでアクセスできる領域をホワイトリスト形式で制限することができます。（残念ながら、`読み取りのみ`や`削除禁止`などの細かい制限はできないようです。）

## devサーバーの起動
GraphQL Meshのdevサーバーを起動します。

```
$ yarn mesh dev
```

実行すると、自動的に`localhost:4000`がブラウザで開かれ、`GraphiQL`が立ち上がります。
失敗する場合は、`必要なパッケージがインストールされているか` `MySQLサーバーと疎通できているか`あたりを確認してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316508/f354ee2c-ef44-2a87-0a20-df3b6b1cabf2.png)

## スキーマの確認

Docsを見ると、接続したDBを操作するためのスキーマがいくつか用意されているのが確認できると思います。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316508/9361b283-2e3b-d8ce-fbfa-7bcd3ec5bce6.png" width="300" />
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316508/9c7cb18b-9696-9da9-2cc0-498a829b09a1.png" width="300" />

序文でも書いた通り、これらのスキーマはDB接続時に自動的に生成されるため、GraphQL Mesh側で管理する必要がありません。

# Queryを実行してみる

画面中央のフィールドにQueryを記述し、行を取得してみます。
余談ですが、公式ドキュメントでは`get${テーブル名}`のような定義がされるように例が書かれているものの、実際はテーブル名だけの定義になっているようです。

```graphql
{
  test_table(
    limit: 1,
    where: {
      id: "1"
    }
  ) {
    id
    name
    comment
  }
}
```

実行することで、目当ての行を取得することができました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316508/f3f32c7a-cfa6-5d79-ba06-967271c2bfd3.png)

MySQLのように`limit`や`where`が見られますが、まさにMySQLのクエリと同じ動きをしてくれます。公式ドキュメントによると、これらのGraphQLのスキーマは適切なMySQLのクエリに変換されて、DBに送信されます。上記の例でいうと、下記のクエリに変換されるようです。
```sql
SELECT id, name, comment FROM test_table
WHERE id = '1'
LIMIT 1;
```

基本的には用意されたスキーマに従って記述するだけで、MySQLのクエリと同じように行の取得が可能です。CodeSandboxにあげられているexampleを見る限り、JOIN句と同等の取得にも対応しているため、ある程度複雑な内容であっても対応できそうです。

https://codesandbox.io/examples/package/@graphql-mesh/mysql

# Mutationを実行してみる

MySQLで言うところの`UPDATE` `INSERT` `DELETE`はMutationで行います。
テーブルごとにそれぞれの命令に相当するスキーマが定義されているため、必要に応じたスキーマを使う必要があります。

今回は`UPDATE`を実行します。

```graphql
mutation update {
  update_test_table(test_table: {
    name: "updated",
    comment: "変更します"
  },
  where: {
    id: "1"
  }) {
    name
    comment
  }
}
```

実行することで、変更差分が返されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316508/0e1923d7-1d0c-7b7c-e032-593f5bef7bc9.png)

前段のQueryで再取得すると、内容の変更が確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/316508/3b59fbd7-a7eb-5d0e-44f4-8573c054a9c2.png)

Mutationに関しても、Queryと同様に用意されたスキーマに従って記述するだけでDBの操作を行うことができます。
`INSERT`と`DELETE`も下記のように記述すれば実行できました。

```graphql:INSERT
mutation insert {
  insert_test_table(test_table: {
    id: "2",
    name: "second",
    comment: "テスト"
  }) {
    id
    name
    comment
  }
}
```

```graphql:DELETE
mutation delete {
  delete_test_table(
  where: {
    id: "2"
  })
}
```

# 懸念点
現時点では、いくつかの懸念点があると個人的に感じました。

- 単体では、認可の仕組みを持てない。
    - 参照できるテーブルとカラム以外に関しては、これ単体では制御できないので、パブリックな場で公開するようなサービスで使う場合は誰に触られても良いような領域でしか使わない、もしくは別でそのあたりを制御する仕組みを用意する必要がありそうです。
- INSERTがAUTO_INCREMENTに対応していない。
    - idを`AUTO_INCREMENT`かつ`NOT NULL`にするというのはかなりよくあるケースだと思うのですが、`insert`のMutationを実行時にidが無指定だとエラーになって怒られてしまいます。ここは中々不便。
- トランザクションが使えない。
    - 構造的に当然といえば当然ですが、トランザクションが使えないので複雑な更新処理などは難しいです。

# まとめ
GraphQL Mesh経由でMySQLを操作する方法のご紹介でした。（MySQLをGraphQLでラップしたいというのも、それなりにニッチな需要な気がしますが、）実装とスキーマの管理不要で簡単に導入できるので、狭い領域や個人用であれば選択肢に挙げられる便利なツールだと思います。