リソース
リソースGraphQLベストプラクティス

GraphQLベストプラクティス

GraphQLは十分に成熟しており、長年にわたって普及してきたため、コミュニティからベストプラクティスを共有する多くの記事が公開されています。これらのガイドは、スキーマ設計、命名規則、セキュリティ対策、意味のあるエラーの提供など、GraphQLのほぼすべての側面を網羅しています。

以下は、GraphQLのベストプラクティスに関する最も説得力のあるガイドの一部です。

graphql.orgのベストプラクティス

GraphQLの公式サイトでは、GraphQLのベストプラクティスへの一般的な入門を提供しています。

これらの項目は主に高レベルの関心事を扱っており、例えば以下のようなものがあります:

アーキテクチャ内でGraphQL層が配置される場所

Lee Byronの提言

GraphQLを世界にリリースして間もなく、GraphQLの生みの親であるLee Byronは記事 Lessons From 4 Years of GraphQL を発表し、GraphQLをどのように概念的に扱うべきかを説明しました:

  • 命名は重要である
  • エンドポイントではなくグラフで考える
  • ビューではなくデータを記述する
  • GraphQLは薄いインターフェースである
  • 実装の詳細を隠す

また、FacebookでGraphQLを使用しながら学んだ貴重な原則やレッスンも詳しく説明しています。

GraphQL Rules

GraphQL Rules は、GraphQLを使った日常的なベストプラクティス、主にGraphQLスキーマの設計に特化したサイトです。

このリソースは非常に詳細です。いくつかの優れたリソース(Designing GraphQL Mutations という記事や、Shopifyのチュートリアル Designing a GraphQL API など)からの情報をまとめ、簡潔に提示しています。

記載されているルールには以下が含まれます:

  1. 命名ルール
    • GraphQLのフィールドと引数には camelCase を使用する。
    • GraphQLの型には UpperCamelCase を使用する。
    • ENUMの型名には CAPITALIZED_WITH_UNDERSCORES を使用する。
  2. 型のルール
    • 特定のセマンティック値を持つフィールドや引数を宣言する場合は、カスタムスカラー型を使用する。
    • 特定の値の集合を含むフィールドにはEnumを使用する。
  3. フィールドのルール(Output)
    • フィールドにはセマンティックな名前を使用し、フィールド名に実装の詳細が漏れないようにする。
    • フィールドが常に特定の値を持つ場合は、Non-Null フィールドを使用する。
    • 関連するフィールドはできるだけカスタムObject型にまとめる。
  4. 引数のルール(Input)
    • 関連する引数は新しいinput-typeにまとめる。
    • 引数には厳格なスカラー型を使用する(例:String ではなく DateTime)。
    • クエリの実行に必要な引数は required としてマークする。
  5. リストのルール
    • リストのフィルタリングには、すべての利用可能なフィルターを含む filter 引数を使用する。
    • リストの並べ替えには、Enum または [Enum!] 型の引数 sort を使用する。
    • デフォルト値を持つ limitskip を使用して、リスト内の返却アイテム数を制限する。
    • ページネーションには pageperPage 引数を使用し、items(要素の配列)と pageInfo(メタデータ)を含むoutput型を返す。
    • 無限スクロールのリストにはRelay Cursor Connections Specificationを使用する。
  6. Mutationのルール
    • 単一リソース内のmutationをグループ化するにはNamespace-typeを使用する。
    • CRUDを超えて考える — リソースに対するさまざまなビジネス操作のための小さなmutationを作成する。
    • 複数のアイテムに対してmutationを実行できる機能(同一型の一括変更)を検討する。
    • Mutationはすべての必須引数を明確に記述し、どちらか一方というオプションがあってはならない。
    • Mutationでは、すべての変数を1つのuniqueなinput引数に入れる。
    • すべてのmutationはuniqueなpayload型を持つべきである。

リゾルバーのベストプラクティス

記事 GraphQL Resolvers: Best Practices では、フィールドリゾルバーを最適に作成する方法を説明しています。Node.jsサーバー(PayPalのインフラはExpressベース)を対象としていますが、PHPを含む他の技術にも適用できるレッスンが多く含まれています。

主な要点は以下の通りです:

  • 親から子へのデータの取得と受け渡しは慎重に使用する。
  • dataloader などのライブラリを使用して、下流へのリクエストの重複を排除する。
  • データソースにかけている負荷を把握する。
  • 「context」を変更しない。一貫性があり、バグの少ないコードを確保する。
  • 読みやすく、保守しやすく、テストしやすいリゾルバーを書く。複雑にしすぎない。
  • リゾルバーをできるだけ薄くする。データ取得ロジックを再利用可能な非同期関数に切り出す。

OWASP - GraphQL Cheat Sheet

OWASP(Open Web Application Security Project)は、ソフトウェアのセキュリティ向上に取り組む非営利団体です。さまざまな技術がどのようなエクスプロイトに対して脆弱であるかを調査し、セキュリティ問題の解決策を詳しく説明することで、開発者が攻撃を防ぎやすくしています。

OWASPは GraphQL Cheat Sheet を公開しており、GraphQLにおける最も一般的な攻撃と最大のセキュリティ問題、およびそれらへの対処方法を説明しています。

GraphQLに対する一般的な攻撃には以下が含まれます:

  1. インジェクション — これには通常、以下が含まれますが、これに限りません:
    • SQLおよびNoSQLインジェクション
    • OSコマンドインジェクション
    • SSRFおよびCRLFインジェクション/リクエストスマグリング
  2. DoS(サービス拒否)
  3. 壊れた認可の悪用:IDORを含む、不適切または過剰なアクセス
  4. バッチ攻撃(Batching Attacks)、GraphQL固有のブルートフォース攻撃手法
  5. 安全でないデフォルト設定の悪用

OWASPはそれぞれを回避するための推奨事項を提供しています。

GraphQLクエリのベストプラクティス

Apolloチームは GraphQL query best practices を公開し、GraphQLクエリを構成する実践的な方法についての洞察を提供しています。

例えば、以下の2つのクエリは同じ目標を達成しますが、最初のクエリにはオペレーション名があるため、デバッグ時により理解しやすく役立ちます:

# Recommended ✅
query GetBooks {
  books {
    title
  }
}
 
# Not recommended ❌
query {
  books {
    title
  }
}

提案には以下が含まれます:

  • すべてのオペレーションに名前を付ける
  • GraphQLの引数を提供するには変数を使用する
  • 必要な場所で必要なデータのみをクエリする
  • 関連するフィールドのセットをカプセル化するにはfragmentを使用する
  • グローバルデータとユーザー固有のデータを別々にクエリする

one graphの活用

同じくApolloチームによるサイト Principled GraphQL では、GraphQLは単なる仕様ではなく、おそらくより重要なこととして、企業のデータ「グラフ」とやり取りするためのインターフェースであることを説明しています。

10の原則のリストを通じて、このサイトはシングルグラフを最大限に活用する方法を説明しています:

  1. One Graph:企業は、各チームが作成した複数のグラフではなく、1つの統合されたグラフを持つべきである。
  2. Federated Implementation:グラフは1つだけであるが、そのグラフの実装は複数のチームにわたって連携されるべきである。
  3. Track the Schema in a Registry:グラフを登録・追跡するための単一の情報源があるべきである。
  4. Abstract, Demand-Oriented Schema:スキーマは、サービスの実装の詳細を隠しながら、コンシューマーに柔軟性を提供する抽象化レイヤーとして機能すべきである。
  5. Use an Agile Approach to Schema Development:スキーマは実際の要件に基づいて段階的に構築され、時間をかけてスムーズに進化すべきである。
  6. Iteratively Improve Performance:パフォーマンス管理は継続的なデータ駆動型のプロセスであり、変化するクエリ負荷やサービス実装にスムーズに適応すべきである。
  7. Use Graph Metadata to Empower Developers:開発者は、開発プロセス全体を通じてグラフに関する豊富な認識を持つべきである。
  8. Access and Demand Control:クライアントごとにグラフへのアクセスを許可し、クライアントが何にどのようにアクセスできるかを管理する。
  9. Structured Logging:すべてのグラフオペレーションの構造化ログを収集し、グラフの使用状況を理解するための主要なツールとして活用する。
  10. Separate the GraphQL Layer from the Service Layer:グラフ機能を各サービスに組み込むのではなく、独立した層として分割したレイヤードアーキテクチャを採用する。