📖

GraphQL / Hasura の社内勉強会を開催しました

2024/06/07に公開

株式会社GENDA FE/BEエンジニアの shinnoki です。

先日社内で GraphQL および Hasura の勉強会を開催したのですが、自分にとっても知識をまとめるいい機会となったため、その際のスクリプトを記事として公開いたします。
とりとめのない記事ではございますが GraphQL や Hasura の導入検討にご利用いただければ嬉しいです。

なお自分の知識をもとに十分な裏取りをせず書いた内容もございますので、誤った情報がございましたらコメントにてご指摘ください。

開催の背景

GENDA グループはエンターテイメントという広い領域の中でさまざまな事業を展開しており、それに伴い多くのプロジェクトやプロダクトが存在しています。
私の所属している FE/BE (フロントエンド/バックエンド) Chapter では、技術選定なども含めて最適な実装手段によってプロダクトを開発し、ユーザーに価値を届け続けるという役割を担っています。
そのような中で私が携わっている デジちゃいむ は GraphQL と Hasura というグループ内でも珍しい技術を採用しており、普段関わっていないメンバーも概要を理解しておくことで今後の技術選定に活用できるのではという声があり勉強会の開催に至りました。

ご参考までに講師(私)は以下のようなレベル感です。

  • React / React Native: 7年
    • ちょうど Class Component → Function Component への移り変わりを見てきた
    • REST API との繋ぎこみ以外に Firebase や AWS Amplify の利用経験もあり
  • GraphQL: 5年
  • Hasura: プロダクションで4年以上運用

勉強会内容

GraphQLについて

  • (参加者5名のうち) GraphQL を使ったことがある人は?
    • 1名: プロジェクトで使ったことがある
    • 1名: GitHub API v4 を触ってみたことがある
    • その他: なし
  • GraphQL - Wikipedia
    • GraphQL は、2012年に Facebook の内部で開発され、2015年に公開された。

    • 名前の由来はグラフ理論 (Graph Theory) から
  • GraphQL の生まれた背景
    • モバイルアプリやリッチなウェブサイトの普及により、クライアント(フロントエンド)からバックエンドのAPIにアクセスしデータを取得したり処理を行ったりする場面が増えてきた
    • 複数の画面やクライアントから同じようなデータ取得や処理を行う際はAPIを使いまわしたいがアンダーフェッチ(欲しいフィールドが取得できない)やオーバーフェッチ(逆に不要なフィールドを取得してしまう)などが問題になることが多かった
      • /users/1?field=id,name のように REST API のパラメータでフィールドを指定するような実装がプロジェクトによってバラバラに行われていた
  • GraphQLスキーマとクエリ https://graphql.org/ より
    • スキーマ: データ型、オペレーションの定義
      • Query: 読み取り専用
      • Mutation: 書き込み
      • Subscription: リアルタイムの読み取り
        • GraphQL Subscriptions over Websocket が一般的
        • AWS AppSync は登場初期は GraphQL Subscriptions over MQTT over Websocket だった
    • クエリ(ドキュメント?オペレーション?)[1]: 問い合わせの内容
      • 1つのクエリに Query / Mutation / Subscription を共存させることはできず、読み取り処理と書き込み処理が CQRS のように強制的に分離される ことも特徴
  • 技術としては単純に HTTP POST のリクエストボディとしてクエリを送り application/json が返ってくるだけで、何か特別なことをしている訳では無い
    • 大抵の場合 POST /graphql ような単一エンドポイントに対してリクエストを送る
  • GraphQL サーバーの実装
  • React の GraphQL のクライアント
    • Relay
    • Apollo Client
    • Urql
    • だいたい(React Server Components 以外では) useQuery のような Hook でデータを取得する
      • TanStack Query や SWR よりちょっと前からこのスタイル編み出していたような気がする(ちゃんと調べた訳ではなく要出典)
  • GraphQL を使うメリット
    • GraphQL Code Generator を用いた型生成 (主にTypeScript) との親和性が高い
      • フロントエンドとバックエンド間の API 通信の型付けは近年必須級に求められるようになった
      • OpenAPI や gRPC 等でも同様のメリットを得られるが GraphQL は最初からグラフ構造なためリレーションの取得の表現力が高い
  • GraphQLのデメリット
    • クライアント側で自由にクエリを組み立てられるため、重い処理を投げつけられる危険性がある
      • 複雑度の制限、ホワイトリストなどの対策が必要
      • 認証が必須だったりIP制限などのアクセス制限をかけられる場合はある程度許容できる
    • エンドポイントが単一だったり、エラー時もステータスコード200でレスポンスが返ってくるため、エラー処理やロギングで独自のノウハウが必要

Hasura (Hasura GraphQL Engine) について

  • Hasura GraphQL Engine Documentation
  • DB に接続するとほぼ自動的に GraphQL API を提供してくれる Web サーバー (もしくはクラウドサービス)
    • Hasura GraphQL Engine のことを指して Hasura と呼ぶことが多い
    • 公開事例は少ないが、スタートアップを中心に採用事例はじわじわ増えている
    • ちなみに Hasura 社はインドの会社
  • セルフホスティング版の Docker Imege とクラウド版が存在
    • Pricing that scales with your needs - Hasura
    • Self-Hosted
      • Community Edition (CE) ← 今回触るのはこれ
      • Enterprise Edition (EE)
    • Hasura Cloud
      • Free
      • Professional
      • Enterprise
    • DB は基本的には PostgreSQL 互換を使用
  • CE でも十分使えるが EE/Cloud でのみ使える機能も存在
  • 現在の安定版は v2 で v3 の alpha テストが進行中
    • Announcing Open Source Hasura GraphQL Engine v3
    • 元々 Haskell で実装されていたが v3 は Rust で書き直されているらしい
      • Hasura の名称自体 Haskell + 阿修羅が由来[2]なのに...

ハンズオン: Hasura CE を触ってみよう

  • サンプルリポジトリを用意しました
    • 興味のある方は触りながらこの章の内容を見てみてください

https://github.com/shinnoki/hasura-example-2024

  • Quickstart with Docker
    • HASURA_GRAPHQL_ENABLE_CONSOLE: "true" に設定し http://localhost:8080/console を開くように誘導されるがこれは罠!
    • 実際には必ず Hasura CLI 経由でコンソールを開くことになるので HASURA_GRAPHQL_ENABLE_CONSOLE"false" にする!
  • Hasura CLI のインストール
    • Install / Uninstall the Hasura CLI | Hasura GraphQL Docs
    • バイナリを落としてくるか npm でインストールするか
    • サンプルリポジトリでは package.json に追加したので yarn install でインストール
      • hasura-cli パッケージはサードパーティで管理されているので追従が遅い
      • 今回もセットアップ時の最新版 2.38.0 だと壊れていたので少し古いバージョン 2.35.1 を追加
    • 講師は普段は asdf でインストール
  • hasura console コマンドでコンソール立ち上げ
    • サンプルリポジトリでは npm でインストールしているため yarn hasura console になります
  • マイグレーションとメタデータ
    • hasura console コマンドで立ち上げたコンソール内で操作するとマイグレーションの SQL ファイルやメタデータの YAML ファイルを自動で生成してくれる
      • 前述の http://localhost:8080/console ではファイルが生成されないので注意
    • cli-migrations タグのついたバージョン Docker Image を使うとイメージの起動時に自動でマイグレーションを適用してくれる
      • この際 /hasura-migrations/hasura-metadata のマウントが必要
  • 認証、認可
    • JWT ベースの認証 が基本
      • JWT の payload として custom claim を実装する必要がある (初学者に少しハードルが高いポイント)
    • 「全ユーザーが SELECT 可能だが、ユーザー ID が一致する場合のみ INSERT, UPDATE, DELETE が可能」などの Row Permission が設定可能
      • YAML のメタデータとして設定が出力されるのでチーム開発ではこれをレビューすることになるが、結構大変
      • 機密データが漏れないことを保証したいときは Regression Test を整備したほうがよい
  • ビジネスロジック
    • ActionsRemote Schemas
      • 自動生成される Mutation にはビジネスロジックを挟む余地が少ないので、別でサーバーを立てて処理を移譲する
      • だんだん複雑になっていくのを見越して Mutation は Actions / Remote Schemas に寄せておくというのがよくあるパターン
      • 複数データを同時に挿入したいというだけであれば複数の Mutation をひとつのリクエストに押し込むことで上から順番にトランザクションとして実行することも可能
    • Actions
    • Remote Schemas
      • 他に立てた GraphQL Server へのリクエストを Hasura 経由で呼び出すことで JOIN したり認証、認可を適用できる
      • 他の GraphQL Server を自分たちで立てなければいけないハードルがあるが DB のデータと JOIN して呼び出せるのが強み https://hasura.io/docs/latest/remote-schemas/overview/ より
  • 非同期処理
    • Event TriggersScheduled Triggers
    • 便利だがイベント駆動の実装を多用するとコード上で追いづらくなるので、まずは Actions などで同期的に実装することを検討してみるのがおすすめ

Hasura の pros / cons

  • pros
    • とにかく爆速でGraphQL APIを作成できる
      • 今回は Hasura のマイグレーションを機能を見たが、別のバックエンドサーバーが既にある場合でもマイグレーションを Hasura で持たずに横付けで GraphQL API を生やすことができる (実運用したことはないがかなりオススメの使い方)
    • リアルタイム通信を簡単に実装できる
      • GraphQL Subscriptions over Websocket
        • Live QueriesStreaming という用途に応じた2種類の API
        • 自前で Websocket のリアルタイム通信を実装しようとすると考慮しなければいけないことが多い
        • Apollo Client の場合 Subscriptions を使わなくても pollInterval を指定するだけで簡単にポーリングすることもできる
  • cons
    • ロジックがフロントエンドに染み出しがち
      • ある程度許容した上で、重複する処理を関数として切り出すなどの工夫が必要
    • 画像アップロードには対応していないので実装に工夫が必要
      • 別途 S3 などにアップロードし URL を Mutation に渡すなど
    • セキュリティ要件によっては叶えるのが難しい場合がある
      • この点において IP 制限のある社内システムのような用途のほうが懸念を払拭しやすい
  • 似たようなメリットを持つものとしての比較対象は Firebase や AWS Amplify などの mBaaS
    • 下記の表は2020年ころにまとめたもの(古いので注意) https://speakerdeck.com/shinnoki/hasura-tohahe-zhe-ka-meritutodemerituto?slide=11
    • 立ち上げ時から Cloud Firestore や Amazon DynamoDB を採用して設計や運用が課題になるケースが多く、その点データベースが PostgreSQL
      • もちろん NoSQL にも素晴らしいメリットはあるのだが、適材適所
    • 近年 Firebase や AWS Amplify 側が Hasura に寄ってきた印象
      • AWS AppSync から Aurora に接続 (割と前からできていた?)
      • RDS Data APIによるスキーマ生成 (2023年11月)
      • Firebase Data Connect (2024年5月←NEW!)
    • Supabase もある
    • 銀の弾丸ではないが、用途がハマれば Hasura は最強 (個人の感想)
  • 余談だが Prisma は Hasura と書き味は似ている
    • Prisma は今でこそ ORM ライブラリのような顔をしているが v1まで Hasura のような GraphQL サーバーだった (と記憶している)
    • Prisma を使ってバックエンドを実装したほうが柔軟性はある
    • React Server Components + Prisma は Developer Experience が高そうな可能性をバリバリ感じる

GraphQL の今後

  • GraphQL と React Server Components
    • 一見無関係なようで、似たような課題を解決するために同じ Facebook (Meta) から生まれた技術
    • 現状は Next.js App Router を使う場合のみ GraphQL を採用するモチベーションが相対的に下がるが、現実的にはネイティブアプリや従来の SPA なども考慮する必要があるケースが多いため、まだまだ使われていくと思われる

感想、質問

感想

  • Hausra できること多すぎ!最強じゃん!俺もHasuraになりたい!
  • 簡単にデータ取れるのは感動
  • Hasura Console でサイドバーぼちぼちするとクエリ補完されるのは便利!
  • GraphQL 初心者でもすぐバックエンドを構築できちゃうのは神
  • 管理画面で採用したい
  • Hasuraを 使うなら PostgreSQL が良さそうだが、今まで MySQL や MariaDB を使っていたのでちょっと不安
  • 規約が多いので、レールから逸れたこととか細かいことをやりだすとメリットが享受しづらそう
  • 直接関わっていたわけではないですが元々 GraphQL を使ってスキーマ設計の難易度が高くやめたというプロジェクトを知っているので、テーブルから自動生成されるのはよさそう

質問

  • Actions を使う場合 Hasura → REST API の通信のセキュリティってどう担保するんでしょうか
    • 野ざらしよりは x-api-key のようなヘッダーを使って既定の文字列がセットされていなければ弾くようにすると良いです
    • 同じ VPC 内のプライベートネットワークで通信を閉じることができればより強固です
  • Actionsで、データ取ってから、ロジックを元にデータを修正して返すみたいなこともできるんですか?
    • できます!
  • 生成されるクエリ確認とかチューニングできるのか気になりました
    • Hasura Console上でAnalyzeボタンを押すとSQLの確認ができます

    • チューニングは割と厳しいですが PostgreSQL の View や SQL functions を使う機能があるのでその辺りを使うことになります

      • ただしこれらはコードではなくマイグレーションとして残ることになり見通しが悪くなりがちで、使うかどうかは慎重に検討したほうが良さそうです

まとめ

1時間で予定していたうち講義パートが55分くらいで時間的にはちょうどよかったのですが、感想を出し合ったりディスカッションの場が少し足りなかったので、もう少しコンパクトに進行できればよかったです。
また GraphQL の真価はフロントエンドも含めて見た開発フローにおいて十分に発揮されるので GraphQL Code Generator との組み合わせのハンズオンもできればよりイメージが湧きやすかったのかなと思いました。

GraphQL も Hasura も決して全てのシーンにおいてオススメするものではないというのは念を押しながらも、用途がハマればとても強力なツールだと思っています。

脚注
  1. 用語の使われ方に揺れがある気がしますが、ここでは読み取りオペレーションを Query、リクエスト全体をクエリと呼ぶことにします ↩︎

  2. https://hasura.io/about/ ↩︎

GENDA

Discussion