Hasuraとは?
Hasura社が作成しているミドルウェアで、クライアントとDBの間に入ってGraphQLとSQLを相互変換してくれます。
Hasuraの裏側にデータベースが存在します。
メリット
DB定義からGraphQLスキーマを自動生成でき型安全な実装が可能
DBスキーマを定義したらそれを元にGraphQLのスキーマを自動生成してくれるのでスキーマ定義が間違っているかどうかの心配がない。PostgreSQL関連のこととGraphQL関連の型定義を同時に自動でやってくれるので型安全であると言えます。
開発時間の短縮が可能
自分でGraphQLのリゾルバ関数を書かなくてもよくなるので、GraphQL APIの開発時間を短縮することが可能です。
Hasuraを採用した方が良い例
新規サービス開発時
ほとんどのサービスは新サービス時は省リソース、スピード優先になる。
Firebase、FireStoreとの比較
新規サービス時はそこまでデータ量もないのでそこまでNoSQLのようなスケーラビリティは考慮しなくても良い。また、HasuraならFirebaseと違ってスキーマができて変更や影響箇所などを明確にすることができます。Firebaseのようにリレーションができないということはない。
小さいチーム
フロントエンドエンジニアがHasuraのスキーマも変更するのでバックエンド、フロントを兼任するような小さいチームが対象になります。
製品の種類
Hasura Cloud
Hasuraのクラウドベースのホスティング環境、Cloud版であれば以下のようなことができます。
- GraphQLサブスクリプションのパフォーマンスを見れる。
- データベースをread/write以下に変えることができる。
料金
基本無料で有料でも1ヶ月99ドル(GCPのCloud Runよりはかなり安め)
OSS版 Hasura
OSS版のHasura、こちらであれば別クラウド環境などにインストールして構築することもできます。
ライセンス
Apacheライセンス2.0
機能
Database
Hasura上でDBにテーブル定義するとGraphQLスキーマに自動変換されて、GraphQL APIで使用できるようになります。
RESTified GraphQL Endpoints
GraphQLエンドポイントをRESTエンドポイントとしても公開が可能です。
Actions
外部のREST APIのエンドポイントを叩きたい場合に使います。
基本的にはRemote Schemaで良いと思います。ただ、バックエンドのビジネスロジックが相当薄い簡易処理の場合は使えるかもしれません。(ただ、そういう場合はもはやフロントエンドにビジネスロジックすら持たせても良いのかもしれないのでやはり基本は使わない)
なお、Actionの結果を用いてQueryやMutationなどと連携させることもできます。
ユースケース
Cloud Functionなどのサーバーレス技術を使う場合などはRESTからは変えられないので採用しても良いかも。
Remote Schemas
外部のGraphQLエンドポイントを叩きたい場合に使います。
GraphQL ServerをHasuraとは別に立てます。ただ、この機能を使うことであたかもHasuraに内蔵されている処理であるかのように振る舞わせることが可能です。
基本は更新時のビジネスロジックの実装はこれを使って行うことになる。Hasuraとは独立したバックエンド処理になるので普通にGraphQLのリゾルバ関数を記述することになります。
Actionsは外部のREST APIを呼び出すときに使っていましたが、Remote Schemasは外部のGraphQL APIを呼び出すために使います。
よくある構成
Apollo Server + Expressなどで実装し、Vercelなどにホスティングする。
Event Triggers
データベースに対して変更があった場合に対象のwebhook(いわゆるREST API)に対してリクエストを発行します。一見乱雑に見えるが、全部ログとして残るので障害に強い設計になる。(Hasuraが提唱している3factor Appという設計原則に則っている。)
ユーザーにはJSON形式でのペイロードが送られます。発生前後の値が含まれるのでボイラープレートとしても利用が可能になる。
- DBで特定のイベント(DELETE、UPDATE、INSERT)が発生した際にあらかじめ定義しておいた外部APIを呼び出す機能
- 外部APIへの通知はat least once方式(少なくとも1回は通知する方式)で通知する。
ユースケース
- 会員データ登録後のユーザー通知(メール送信など)
- データの更新にトリガーしてキャッシュを温める。
よくある構成
Webhookを呼び出すことになり、GraphQLではなくREST APIになるので自分でホスティングする必要があります。
Scheduled Trigger
ユーザーが定義したロジックを指定した時間に実行する機能。各イベントのステータスやログはHasuraコンソール上で見れます。リトライ回数、インターバル、リトライ期限なども設定できるためジョブ管理サーバーとしても運用できる。
cron triggers
定期実行、例えば毎日24時にスタミナが回復するゲームを作る場合など。
one-off scheduled events
設定した日時に1度だけ実行。Hasura1.3から実装されました。
デプロイする。
Hasuraではデプロイする際のガイドや本番環境でセキュアなGraphQLエンジンを提供するためのガイドが含まれている。
Set an admin secret
- コンソールやエンドポイントへの不正アクセスを防ぐために使用する。
- Hasuraでユーザー認証を行う際には必ず設定する。
ちなみに、公開サーバーにしたい場合はこれを使わなくてもロールを設定するとできる。
Enable HTTPS
Hasura自身はAPIをSSL/TLSで保護できないためAPIをセキュアにするにはリバースプロキシを設定する必要がある。
Configure Logging
Hasuraではquery-log(Hasuraのquery全般に関するlog)やhttp-log(HTTPにアクセスした際のlog)等の12種類提供されておりデプロイする際に設定できる。
本番運用
データベーススキーマの管理
バックエンドのORMとHasuraのスキーマの間にインピーダンスミスマッチが発生する。バックエンドからHasuraスキーマは見れないので。Hasura側からもバックエンドのスキーマは見れません。
対策
どちらかをメインにしてどちらかをサブにする。一応、Hasura側からもマイグレーションは作成することが可能です。
Remote Schema
参照系
スキーマ定義がそこまで複雑ではないのでRemote Schemaは別に使わなくても良い。
集計などクライアントでやりたくなり処理だけはRemote Schemaでやる。
更新系
複雑なビジネスロジック
クライアントではなくほぼRemote Schemaでやる。
マスタデータの更新は入力を保存するだけの場合
Hasuraのmutation機能で十分なことが多い。
権限管理
参照系
Hasuraの権限管理を使う。
レコードカラムとJWTのカスタムclaimを見比べてかなり柔軟にアクセス制御を行わせる。
更新系
ほぼRemote Schemaで処理を管理することになるので基本バックエンド側でアクセス制御を行う。
ロールごとにschemaを作る。
Introspectionクエリを投げる。
メディアアップロード、ダウンロード
ここだけはRESTにしてRemote SchemaのサーバーにHasuraを介さずに投げる。
Rails連携
一般的には以下のような役割分担で実装することが多くなるでしょう。
Rails
- ビジネスロジック
- マイグレーション
Hasura
- シンプルなCRUD操作
- 複雑な処理はActionsでRailsに流す。
- 「Remote Schemas」でRailsの処理を取り込むのはあり。
DB
基本はPostgreSQLになる。MySQLやAuroraなど他のDBも対応し始めたが、基本PostgreSQLの後追いになり機能拡張にラグが発生します。
連携の流れ
以下のような流れになります。Railsと連携するというよりはPostgreSQLとの連携という方が近いかもしれません。
- Railsのmigrationコマンド(rails db:migrate)を実行してPostgreSQLに変更を反映させる。
- HasuraはPostgreSQLの変更を同期して読み取る。
インストール
1 2 |
gem 'graphql' gem 'graphiql-rails' |
生成コマンド
1 |
docker-compose exec web rails generate graphql:install |
特定のモデルを生成
すでにDBにデータが存在している場合
1 |
docker-compose exec web rails g graphql:object Customer |
そうすると「app/graphql/types/customer_type.rb」というパスに以下の内容のファイルが生成されます。
1 2 3 4 5 6 7 8 9 |
module Types class CustomerType < Types::BaseObject field :id, ID, null: false field :name, String, null: false field :email, String, null: false field :created_at, GraphQL::Types::ISO8601DateTime, null: false field :updated_at, GraphQL::Types::ISO8601DateTime, null: false end end |
なお、すでにDBにテーブルが存在していない場合はカラムの指定などが必要になります。
この記事へのコメントはありません。