バイセル Tech Blog

バイセル Tech Blogは株式会社BuySell Technologiesのエンジニア達が知見・発見を共有する技術ブログです。

ヘッダー画像

Hasura でプロダクト開発をしてみて感じた良かったところ・辛かったところ

はじめに

こちらは バイセルテクノロジーズ Advent Calendar 2021 の 9 日目の記事です。 前日の記事は 飯島さん の「新規プロジェクトにインフラ監視ツールを導入した話」でした。

こんにちは。2021 年に新卒で入社した開発 2 部の小松山です。 私は今年の 6 月末までバックエンドは Ruby on Rails がメインのプロダクトの保守開発業務を行っていたのですが、7 月から新しいプロジェクトへ配属され、新規プロダクトの開発に携わることになりました。そこではバックエンドに Hasura を採用しています。この記事では、その Hasura を実際にプロダクト開発に利用して見えてきた、メリット・デメリットを紹介できればと思います

Hasura Logo

目次

前提

Hasura の概要

Hasura 自体を紹介する記事はたくさん上がっているので、ここでは簡潔な説明にとどめておきます。

  • オープンソースの GraphQL サーバ
  • マイグレーションや Seed の管理ができる
  • DB のスキーマに基づいて GraphQL の API (Query / Mutation / Subscription) を自動生成してくれる
  • JWT を使った認可処理ができる
  • Actions / Remote Schemas / Events を利用して自動生成される API を拡張できる

※ 正式名称は Hasura GraphQL Engine のようですが、この記事では便宜上「Hasura」と表記させていただきます。

github.com

hasura.io

プロジェクトでの運用

私のプロジェクトではインフラに GCP を使用しており、Hasura は Cloud Run へデプロイしています。プロジェクト当初は Hasura 社のマネージドサービスである Hasura Cloud にデプロイしていたのですが、Hasura サーバをプライベートネットワークに置くこととなったため、IP 制限ができない Hasura Cloud から Cloud Run に切り替えを行いました (下図参照)。

Hasura のロジックの拡張には Remote Schemas を採用しており、gqlgen (Go) で実装しています。こちらも Cloud Run にデプロイしています。

バックエンドの構成

開発には下記のようなツールを使っています (一部抜粋)。

  • バックエンド

    • PostgreSQL
    • hasura/graphql-engine:v2.0.9.cli-migrations-v3
    • Elasticsearch
    • MinIO (Google Cloud Strorage をモックするためにローカル環境でのみコンテナを使用)
    • Go (Remote Schemas 用の GraphQL サーバを立てるのに使用)
  • フロントエンド

    • TypeScript
    • Next.js
    • graphql-codegen
    • Apollo Client

実際の開発は以下のような流れで進みました。

  1. 画面設計・DB 設計
  2. 設計をもとにテーブルを作成
  3. バックエンド開発 (テーブル修正、Remote Schemas の開発、認可の設定、CI / CD の整備等)
  4. フロントエンド開発

2 のテーブル作成が終わった時点で、Hasura が DB のスキーマに基づいた CRUD の API を自動で作成してくれるため、3 と 4 はほぼ同時進行でした。一部の API は Hasura が自動生成してくれたものでは要件を満たしきれなかった (特にバリデーション周り) ため、一旦 Hasura の生成した API で画面を作り、後から自前で実装した Remote Schemas のクエリに置換する、ということを行いました。

良かったところ

テーブル作成が完了すればフロントエンドの開発を始めることができる

前述の通り、Hasura でテーブル作成さえやってしまえばその時点で API が自動生成されるため、「API の開発が終わるまでフロントの開発を待たないといけない」という場面は少なかったです。自動生成される API は 多彩なユースケースに対応でき、SQL 文を書くように GraphQL のクエリを書けるので、自由度は高いです。おかげでフロントエンド実装者とバックエンド実装者の間での認識齟齬や、バックエンドの実装ミスによる開発の巻き戻しが少なかったように思います。

hasura.io

hasura.io

認証・認可処理の実装の手間を省ける

Hasura では JWT ベースの認証・認可を簡単に設定することができます。Hasura サーバの環境変数 HASURA_GRAPHQL_JWT_SECRETJWT Config の値 (JSON) を設定するだけで JWT による認証・認可を有効化することができます。また、テーブル単位でデータへのアクセス制限ルールを設けることができるので、API の権限設定が楽に実現できました。Remote Schemas にはフィールド単位で制限をかけることができます。このレベルの認可処理をゼロから実装するとなると、かなり骨が折れそうです。

hasura.io

REST API も作成できる

Hasura には自身の GraphQL クエリと REST のエンドポイントを紐付ける機能が備わっています。GUI 上でその紐付けが行えるので設定も手軽です。私のプロジェクトで開発しているプロダクトの API は、社内ですでに運用されている他のプロダクトから呼ばれることを想定しています。その社内プロダクトは GraphQL での通信は未対応でしたが、Hasura サーバが REST のエンドポイントを提供してくれているお陰で、連携の際に GraphQL の知識を持ち込む必要がなくなりました。

hasura.io

しかし、この機能には少し残念なポイントがありました。リクエストパラメータの型に GraphQL 標準の Scalar Types 以外が指定されていると、エラーが返ってしまうことです。

例えば、Hasura が独自に定義する Custom Scalar である bigint 型を REST API のリクエストパラメータに指定して作成します (作成自体でエラーが返ることはありません)。

Create Endpoint

しかし、作成したエンドポイントにリクエストを送ると、エラーが返ってしまいます。

$ curl --location --request GET 'http://localhost:8080/api/rest/users/1' \
--header 'x-hasura-admin-secret: secret'
{"path":"$","error":"Expected bigint for variable id got Number","code":"bad-request"}

この対策として、Custom Scalar なしで定義した Query / Mutation を Remote Schemas で定義・実装し、それを REST と結びつけるようにすることで回避しています。

辛かったところ

拡張ロジックに頼ってしまいがち

Hasura が自動生成する API だけではやりたいことを実現できないということは珍しくありません。特に登録更新系の処理をする際は、GraphQL クエリや DB の型定義以上にデータにバリデーションを行いたくなるケースが多いです。実際私のプロジェクトでは、多くの Mutation は Remote Schemas に自前で実装したものを使用しています。他にも、外部サービスにリクエストを行ってその結果によって処理を分岐したい場合、Hasura が対応していないデータソースに対して CRUD したい場合、前述した REST に紐付けるクエリを作成したい場合など、自前で Remote Schemas に API を作らねばならないケースは結構ありました。結局自前で API を実装するとなると Hasura 旨味である「自動生成 CRUD」を活かすことができません。

私のプロジェクトでは、Query の多くは Hasura が自動生成したものが使われていますが、Mutation の多くは Remote Schemas のものになっています。

レビューがしづらい

ローカルで Hasura の開発をする際は、GUI のツール (Hasura Console) を利用して行います。Hasura Console で Permission の設定や、DB のスキーマの変更の操作をすると、その操作内容がローカルの設定ファイル・マイグレーションファイルに自動で書き出されます。これは開発をするうえで非常に便利である反面、変更差分のレビューがしづらいという欠点があります。マイグレーション・seed のファイルは単純な SQL ファイルなのでレビューは可能ですが、その他の設定ファイルは自動生成された YAML ファイルなので、 Pull Request 上で差分だけ見てもそれが妥当なものなのか、なかなか判断が難しいです。

この対策として私のプロジェクトでは、CI で不正なマイグレーション・メタデータが適用されてしまうことを防ぐ、Hasura の振る舞いテストを書いて期待した動作をすることを担保する、ということをやっていました。

まとめ

いかがでしたでしょうか?GraphQL すらよくわからない状態から、 Hasura を使ってプロダクト開発をしてみていろいろ感じたことをまとめてみました。メリット・デメリットがありますが、個人的にはバックエンドの開発工数を格段に削減してくれる技術として、技術選定の際の 1 つの選択肢に入れてみても良いのではないかと思っています。

私のプロジェクトで開発中のプロダクトはもうリリース目前なので、まもなく二次開発・運用フェーズに入ります。開発段階では見えなかった問題や、その解決策があればまた共有しようと思います。

明日の バイセルテクノロジーズ Advent Calendar 2021 は、鈴木さんによる 「Slack ボットと GAS で癒しを提供する話」 です。

バイセルではエンジニアを募集しています。 hrmos.co hrmos.co