Hasuraがめちゃくちゃ便利だよという話

https://hasura.io/ が使ってみたらめちゃくちゃ便利なのですがあまり使ってる人が多くないようなので、hasura のいいところを挙げていきます。

# hasura って何？

[hasura](https://hasura.io/) は PostgreSQL サーバーから自動的に GraphQL サーバーを建てられるツールです。
Docker イメージとして配布されていて、対象となる PostgreSQL サーバーのアドレスや PW を設定して起動すればすぐに GraphQL サーバーとして使えます。（実際は hasura コンソールから公開するテーブルを選択したりします。詳しくは[doc](https://docs.hasura.io/1.0/graphql/manual/getting-started/index.html)）
ローカルで起動するならこういう感じです。

```sh
docker run -d -p 8080:8080 \
  -e HASURA_GRAPHQL_DATABASE_URL=postgres://username:password@hostname:port/dbname \
  -e HASURA_GRAPHQL_ENABLE_CONSOLE=true \
  hasura/graphql-engine:latest
```

この場合 `localhost:8080` から hasura のコンソールにアクセスでき、 `localhost:8080/v1/graphql` が GraphQL エンドポイントになります。
hasura サーバーがクライアントと PostgreSQL サーバーの間に入って、GraphQL の API で PostgreSQL を操作できるイメージです。

```
client <- (GraphQL) -> hasura server <- (SQL) -> PostgreSQL server
```

例えばこんな単純なテーブルがあったとして、

<img width="263" alt="スクリーンショット 2020-02-12 21.53.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59312/1767154f-9f31-e974-c5e4-61535ddf5ca0.png">

hasura コンソールにある GraphiQL からはこんなふうに GraphQL が使えます。fk を考慮して JOIN したような形のデータを取得できています。

<img width="735" alt="スクリーンショット 2020-02-13 10.08.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59312/5083d75b-45d8-cb96-fce5-a8ac326dd0f3.png">


Query だけでなく、Mutation、Subscription も同様に使えます。

# hasura のいいところ

## PostgreSQL サーバーがあればすぐに使える

hasura は PostgreSQL サーバーと通信してスキーマ情報を解析して自動的に使える GraphQL サーバーとして動いてくれます。
Docker イメージとして配布されているので ECS や Cloud Run、GKE などの基盤を運用しているならあまり負担なく使い始められると思います。
データソースとして PostgreSQL を使っているので、RDS や CloudSQL などのこれまでの運用実績があるプラットフォームを使うことができます。

もし hasura をやめる判断をしたとしても、データソースはただの PostgreSQL なので hasura に強くロックインすることもないと思います。Prisma Cloud のような GraphQL SaaS と比べて辞めやすさが高いのが良いかと思ってます。

## GraphQL サーバーを実装する手間が省ける

hasura が自動で GraphQL サーバーとして動いてくれるので GraphQL サーバーを実装する手間が省けます。
N+1 問題をケアするために Dataloder を使って GraphQL の Resolver を実装するのは結構手間だと思うので、PostgreSQL にテーブルがあって hasura で設定すればすぐに Query, Mutation, Subsucription を使えるというのは開発速度を非常に上げることができます。
実際 hasura を使ってみて、DB をいじって hasura の設定を変更して graphql-codegen でクライアントコードを生成して、タイプセーフかつ高速に開発ができたときは感動モノでした。

その一方で、ほとんどのアプリケーションではただ DB から SELECT, INSERT するだけでは済まないトランザクション内でアレコレする複雑なロジックがあり、それは hasura が生成する GraphQL オペレーションでは難しい場面が出てくると思います。hasura はその解決手段をいくつか用意しています([Remote Schemas](https://docs.hasura.io/1.0/graphql/manual/remote-schemas/index.html), [user-defined SQL functions](https://docs.hasura.io/1.0/graphql/manual/schema/custom-functions.html#custom-sql-functions))。このへんはまだ試せていないところです。

## ページングや集計クエリなども自動で生成される

例えば冒頭で挙げたようなユーザーとコメントを投稿するようなサービスがあったとして、ユーザーが最後にコメントした日時とこれまでのコメント数を表示するユーザー一覧画面を実装したいとします。そのようなクエリも hasura が自動生成するオペレーションで実現できます。

```graphql
query Users {
  users(offset: 100, limit: 20) {
    name
    lastCommentAt: comments(order_by: {createdAt: desc}, limit: 1) {
      createdAt
    }
    totalComment: comments_aggregate {
      aggregate {
        count
      }
    }
  }
}
```

```json
{
  "data": {
    "users": [
      {
        "name": "bob",
        "lastCommentAt": [
          {
            "createdAt": "2020-02-12T13:21:32.555949"
          }
        ],
        "totalComment": {
          "aggregate": {
            "count": 2
          }
        }
      },
      {
        "name": "john",
        "lastCommentAt": [
          {
            "createdAt": "2020-02-12T13:21:47.733137"
          }
        ],
        "totalComment": {
          "aggregate": {
            "count": 2
          }
        }
      }
    ]
  }
}
```

`where`, `order_by`, `limit`, `offset` といった条件句と `max`, `count`, `avg` といった集計がデフォルトで可能です。PostgreSQL が GraphQL として公開される ORM のような使い勝手です。

## 実際に発行される SQL がすぐにわかる

普通の GraphQL サーバーの実装は Dataloder などを介する感じになると思いますが、hasura の場合は GraphQL オペレーションが直接 SQL 文に変換されます。その SQL 文と実行計画を hasura コンソールから見ることができるのでパフォーマンスの分析などもやりやすいのではと思います。

<img width="805" alt="スクリーンショット 2020-02-13 10.02.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59312/defaa3c2-4e24-aede-3067-59a0292e24bb.png">

## 認証認可の仕組みがある

Auth0 などと組み合わせて認証されていないリクエストを弾いたり、ロールベースで機能の制限することなどができます。 [doc](https://docs.hasura.io/1.0/graphql/manual/auth/index.html)
hasura が Auth0 などの認証プロバイダと通信して JWT を取得します。その JWT に含まれた Auth0 から設定したデータを hasura がGraphQLオペレーション実行時に読むことができ、hasura のコンソールでルールを決めます。
JWT に埋め込んだ `X-Hasura-User-Id` を使って自分のデータだけを取得するといったルールが設定できます。

<img width="578" alt="スクリーンショット 2020-02-13 10.03.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59312/f982f4d1-b540-7d15-c481-78d60d653627.png">


このルールが適用されたときに、

```GraphQL
query Users {
  users {
    name
    comments {
      content
    }
  }
}
```

のような全ユーザの名前をコメントを取得するような Query が発行されたとしても `"user_id": { "_eq": "X-Hasura-User-Id" }` の条件が Query に埋め込まれるために他のユーザーのデータが参照されることはないといった感じです。

# hasura のわからないところ

実際使ってみたり、ドキュメントを読み込んでみないとわからない気になるところ :eyes: 

## hasura の設定をどう運用するのか？

ここまで hasura コンソールの Web UI のスクリーンショットをいくつか貼りましたが、実際に運用する段階になったとしてこの UI を本番環境でポチポチするのは避けたいところです。
hasura には Web UI 以外から設定を import/export する機能があり、http 経由で JSON を POST/GET、または CLI から書き込み・読み込みができます。そのいずれかを使って運用することになると思います。
hasura の CLI 付きの Docker イメージ `hasura/graphql-engine:v1.0.0.cli-migrations` は、起動時に `/hasura-migrations` に置かれた設定ファイルを読み込んで起動するようになっています。これを docker-compose と組み合わせて使うとこんな感じになると思います。

```yaml
version: "3.1"

services:
  hasura:
    image: hasura/graphql-engine:v1.0.0.cli-migrations
    volumes:
      - ./hasura/hasura-migrations:/hasura-migrations
    ports:
      - 8888:8080
    restart: always
    environment:
      HASURA_GRAPHQL_DATABASE_URL: postgres://postgres:@postgres:5432/mydb
```

1. ローカルで hasura の設定を変更する
2. 設定をファイルに export して Git にコミットする
3. その設定ファイルが起動時に読み込まれるようなデプロイにする

https://docs.hasura.io/1.0/graphql/manual/migrations/index.html

## その他

疲れたから一旦公開するけどなんかまた追記するかも :innocent: