Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

GraphQL 成熟度モデルの紹介と、プロダクトに当てはめた事例 / GraphQL matu...

GraphQL 成熟度モデルの紹介と、プロダクトに当てはめた事例 / GraphQL maturity model

Hirotaka Miyagi

April 19, 2024
Tweet

More Decks by Hirotaka Miyagi

Other Decks in Technology

Transcript

  1. 自己紹介 Hirotaka Miyagi / @MH4GF 最近はフロントエンド多め 好きな GraphQL の機能はカスタムディレクティブ ROUTE06,

    inc. フルリモートワークの会社で北海道から沖縄まで メンバーが在籍しています! 2
  2. GraphQL 成熟度モデル Meta 社 relay.dev チームの Jordan Eldredge 氏の一連の Tweet

    で紹介された、13 つ のプラクティス juny さんによって日本語訳されたブログ記事が話題に GraphQL 成熟度モデル - とろろこんぶろぐ 4
  3. GraphQL 成熟度モデル 1. サーバーの GraphQL サポート 2. 型やフィールドの文書化・Playground の提供 3.

    GraphQL スキーマと言語の型システムの統合 4. スキーマのデフォルトを nullable にする 5. Node 仕様の実装 6. フロントエンドで正規化されたキャッシュの利用 7. Relay Connections 仕様の実装 8. Fragment を利用した UI のデータ定義 9. 複数の Fragment をまとめ 1 回で取得 10. @defer と @stream のサポート 11. Fragment 単位の Subscription 12. エディタでの Language Server の利用 13. エディタでのフィールド/ 型での定義ジャンプ 5
  4. GraphQL 成熟度モデル GraphQL は、REST API と同じように設計したり、一部機能だけを利用するのでは 価値を最大限に引き出せず、満足度が低くなってしまう このモデルは GraphQL を最大限に活用するための

    13 の達成指標を紹介しています それぞれの項目を紹介しつつ、自分が関わるプロダクトではどう解釈・実装している かを話そうと思います チームやプロダクトの特性に応じて判断は変わるはずなので、ぜひ懇親会でお話しさ せてください! 6
  5. 1. サーバーの GraphQL サポート 1/ Your server is supports GraphQL.

    Your application clients, both mobile and web, are generally able to independently craft their own queries, adding and removing data, without needing to modify server code or worry about impacting other clients. 1/ サーバーが GraphQL をサポートしていることです。アプリケーションのモバイル とウェブの両クライアントは、サーバーのコードを変更することなく、また他のク ライアントへの影響を心配したりすることなく、普通にクエリの作成、データの追 加・削除ができます。 7
  6. 1. サーバーの GraphQL サポート 実現: 提供する機能のほぼ全てを GraphQL を介して実装 バックエンドの GraphQL

    サーバーとしてはgraphql-ruby を利用 フロントエンドの GraphQL クライアントとしてはurql を利用 8
  7. 2. 型やフィールドの文書化・Playground の提供 2/ Your types and fields are well

    documented. GraphiQL acts as an internal interactive playground and documentation for your API. Developers can find the data they need and don’t replicate server behavior in multiple places. 2/ 型やフィールドはきちんとドキュメント化されていることです。GraphiQL は、イ ンターナルな環境でインタラクティブなプレイグラウンドやドキュメントとして機 能します。開発者は必要なデータを見つけることができ、何度も同じサーバー上の 処理を記載する必要はありません。 9
  8. 2. 型やフィールドの文書化・Playground の提供 実現: Playground には Apollo Sandbox を利用、開発用ページでログインユーザーとして 叩けるように

    文書化の例として、カスタムディレクティブを利用してフィールドの認可情報を追加 enum UserRole { MEMBER ADMIN } """ 実行可能なユーザーロールを明示するディレクティブ """ directive @auth(role: [UserRole!]!) on FIELD_DEFINITION | INPUT_FIELD_DEFINITION 10
  9. 3. GraphQL スキーマと言語の型システムの統合 3/ You are using a GraphQL server

    and client which both integrate with their respective languages’ type systems. You now have type safety that spans your network API boundary. 3/ GraphQL のサーバーとクライアントの両方で、それぞれの言語の型システムを統 合して利用していることです。これによりネットワーク API の境界を超えた型安全 性が確保されます。 11
  10. 3. GraphQL スキーマと言語の型システムの統合 実現: フロントエンド: GraphQL Code Generator を利用して型定義・ドキュメントノー ド・MSW

    ハンドラ等を生成 バックエンド: graphql-ruby はコードファーストなフレームワークのため、型を表す Ruby クラスを定義 → スキーマファイルを自動生成 → フロントエンドのコード生成 という流れで型安全性を確保 またモノレポを採用しているため、スキーマの変更時にフロントエンドの CI も実行 し PR 上で破壊的変更を検知できる 12
  11. 4. スキーマのデフォルトを nullable にする 4/ You have adopted the GraphQL

    Working Groups recommended best practice of making your schema nullable by default. Individual field errors encountered on the server generally manifest as small, handleable, UI errors. Your app’s resilience increases. 4/ スキーマをデフォルトで null 許容にするという GraphQL Working Groups 推奨の ベストプラクティスを採用していることです。一般的にサーバー上で発生するフィー ルドのエラーは大したことがなくハンドリングできる UI のエラーです。これにより アプリの回復力( 可用性?) が高まります。 13
  12. 4. スキーマのデフォルトを nullable にする 実現: 意図的に不採用 null 許容にするとフロントエンドでは null チェックのコードが増え可読性が悪化し、

    バックエンドでは null を返すべきではない状況で返していることに気づきづらくなる null 許容にするプラクティスは、主にモバイルアプリなどのレスポンスデータの破壊 的変更が難しい環境を想定されているという認識 Web でのみ提供するプロダクトで即時デプロイが可能であり、デプロイ順序を適切 に行えば問題ないと判断し、一般的な REST API と同様に non-null で設計している 14
  13. 5. Node 仕様の実装 5/ You adopt the Node specification. Most

    objects in your graph have a strong ID. Your clients can easily refetch data about individual objects. 5/ Node の仕様を採用していることです。Graph のほとんどのオブジェクトが強力な ID を持ちます。クライアントは、個々のオブジェクトのデータを簡単に再取得でき ます。 15
  14. 5. Node 仕様の実装 実現: graphql-ruby️ は https://github.com/rails/globalid を利用したグローバル ID 生成が組み

    込まれており、簡単に Node 仕様を実装できるのも魅力 フロントエンドでの単体データの取得は node() フィールドから取得する。不要な フィールドの増加を抑えつつ、実装方法の汎用化が可能 16
  15. 6. フロントエンドで正規化されたキャッシュの利用 6/ Your client GraphQL framework uses the strong

    IDs of the Node specification to store GraphQL data in a normalized form (key ⇒ object). You never have data inconsistency issues between surfaces, and your client uses less memory. 6/ GraphQL クライアントフレームワークが、Node 仕様の強い ID を使用して正規化 された形式(キー ⇒ オブジェクト)で GraphQL のデータを保存していることで す。表示上におけるデータの不整合は発生せず、クライアントのメモリ使用量も少な くて済みます。 17
  16. 6. フロントエンドで正規化されたキャッシュの利用 実現: 意図的に不採用 urql のデフォルトである Document Caching というアルゴリズムを利用している。ミ ューテーションの実行タイミングで関連するクエリを全て再実行する

    正規化されたキャッシュは一部のユースケースでキャッシュを手動操作する必要があ り、キャッシュ操作はバグを生みやすい to B プロダクトでありネットワーク効率よりもバグの発生リスク回避を重視したい 18
  17. 7. Relay Connections 仕様の実装 7/ Your server implements the Connections

    specification for modeling lists (most UIs are glorified lists). Your client GraphQL framework is able to provide robust pagination that works for all your surfaces. 7/ GraphQL サーバー側でリストをモデリングするために Connections 仕様を実装し ていることです(ほとんどの UI は見せかけのリストです) 。 GraphQL クライアント フレームワークは、どのような表示においても機能する堅牢なページネーションを 提供することができます。 19
  18. 7. Relay Connections 仕様の実装 実現: graphql-ruby は Node と同様に Connections

    仕様も簡単に実装できるのも魅力 field :items, Types::ItemType.connection_type のようにフィールド定義するだ けで Connection 型、Edge 型が自動生成される 20
  19. 8. Fragment を利用した UI のデータ定義 8/ Each of your UI

    components defines its own data dependencies using a GraphQL fragment. Queries are composed from these fragments. Components can add/remove data locally without having to worry about breaking anyone else. 8/ 各 UI のコンポーネントが、GraphQL フラグメントを使用してコンポーネントに 閉じた状態でデータを定義していることです。クエリーは、コンポーネントに書か れたフラグメントから構成されます。コンポーネントは、他のコンポーネントに対 する破壊的な影響を心配することなく、コンポーネント内部に閉じてデータを追加/ 削除することができます。 21
  20. 8. Fragment を利用した UI のデータ定義 実現: いわゆる Fragment Colocation と呼ばれるプラクティスで、コンポーネントと

    Fragment を 1:1 になる形で定義し利用 実際に実装してみると設計が難しい状況もかなりあるものの、それを乗り越えたあと のコードの可読性・保守性はかなり高い印象 urql では Colocation を強制するような機能はないため、以下の形でコーディングル ールを定めている GraphQL Code Generator の Fragment Masking で「指定した Fragment に含ま れるフィールドしかコンポーネントで取り出せない」制約を持つ型を生成 turbo gen でコンポーネントコード生成を用意、Fragment を利用するコンポ ーネントのためのボイラープレートも合わせて生成 22
  21. 9. 複数の Fragment をまとめ 1 回で取得 9/ Your client GraphQL

    framework composes your components’ fragments together into a single query per surface. Your UI loads in a single paint without dozens of loading states. Your users thank you. 9/ GraphQL のクライアントフレームワークが、コンポーネントのフラグメントを使 って表示単位ごとに単一のクエリにまとめていることです。UI は、幾重にも重なっ たローディングの状態になることなく、1 回でロードされ表示されます。これによ り、ユーザーはあなたに感謝することでしょう。 23
  22. 9. 複数の Fragment をまとめ 1 回で取得 実現: チームで Page Component

    と呼んでいるルートに近いコンポーネントでだけクエリ を発行し、複数の Fragment をまとめて取得するようにしている 逆に現在複数回ネットワークリクエストが起こる箇所は、例えば「企業を選択した後 にその企業に紐づく社員データを取得してコンボボックス表示する」などのユーザー のインタラクションによってクエリを発行するパターン ローディングについては、urql の suspense モードを利用し React.Suspense で表示 する 24
  23. 10. @defer と @stream のサポート 10/ Your server and client

    both support @-defer and @-stream. UX designers can declaratively opt into nested loading states with one line of code directly in their components only where it improves user experience. 10/ サーバーとクライアントの両方が @-defer と @-stream をサポートしていること です。UX デザイナーは、UX を向上させる場合にのみ、1 行追加するだけでコンポ ーネント内でネストされたローディング状態を宣言的に許容することができます。 25
  24. 10. @defer と @stream のサポート 実現: プロダクト初期フェーズのため現時点では不採用 プロダクトに高度なパフォーマンス要求は今の所求められていないため現時点では不 採用 urql

    と graphql-ruby ではこの二つのディレクティブはサポートされている 余談: React Server Component や Streaming SSR などの技術と @defer ディレクテ ィブは相性が良いと思われるが、運用事例はまだ少ないように見える 今後に期待 GraphQL を Server Components で使いたい - Speaker Deck 26
  25. 11. Fragment 単位の Subscription 11/ Your client GraphQL framework leverages

    each component’s fragment to build targeted subscriptions for each component. Changes to the store result in only the directly affected components rerendering. Your UI is more responsive. 11/ GraphQL クライアントフレームワークが、各コンポーネントのフラグメントを活 用して、各コンポーネントで指定されたサブスクリプションを構築していることで す。バックエンド側のストアに変更を加わると、直接影響を受けるコンポーネント のみが再レンダリングされます。これにより UI がよりレスポンシブになります。 27
  26. 12. エディタでの Language Server の利用 12/ Your client engineers are

    making use of a GraphQL language server in their editors. Fields autocomplete with available data. They can hover over fields to see documentation/types. Deprecated fields render as struck through in their IDE. 12/ クライアント側のアプリを開発するエンジニアが、エディターで GraphQL のラ ンゲージサーバーを活用していることです。各フィールドは利用可能な内容でオート コンプリートされます。フィールドの上にカーソルを合わせると、ドキュメントや型 を見ることができます。非推奨のフィールドは、IDE で波線や取り消し線などのが表 示されます。 29
  27. 12. エディタでの Language Server の利用 実現: VSCode のGraphQL 拡張機能を導入しGraphQL Config

    の設定ファイルを追加するこ とで、マウスホバー時のドキュメントの閲覧や入力時のオートコンプリートが可能 graphql-eslint も利用し、非推奨のフィールドはエディタ上で ESLint のエラー表示が 可能 30
  28. 13. エディタでのフィールド/ 型での定義ジャンプ 13/ Your client engineers’ GraphQL editor integration

    supports click-to-definition for fields/types. They can navigate to the server implementation of a field as easily as they can jump to another client function. 13/ クライアント側のアプリを開発するエンジニアの GraphQL エディターは、フィ ールド/ 型のクリック時の定義ジャンプに対応しています。他のクライアントの関数 にジャンプできるのと同様に、フィールドのサーバー実装に簡単にナビゲートするこ とができます。 31
  29. 13. エディタでのフィールド/ 型での定義ジャンプ 実現: No. 12 と同様に、VSCode の GraphQL 拡張機能を導入し

    GraphQL Config の設定フ ァイルを追加することで Operation 定義 → Fragment 定義 → バックエンドの Schema 定義までコードジャンプが可能 32