GraphQLとは?

profile image

REST APIの限界を克服するために誕生したデータクエリ言語であるGraphQLと、REST APIとの長所と短所を比較しながら探ってみましょう。

この記事は DeepL によって翻訳されました。誤訳があれば教えてください!

GraphQLはFacebookによって開発されたデータクエリ言語で、APIをより高速で柔軟、そして開発者にとって使いやすくするために設計されました。

REST APIとは異なり、クライアントが必要なデータの構造を定義し、単一のリクエストで必要な情報を受け取ることができます。また、タイプシステムを使用してリクエストとレスポンスのデータ構造を明確に定義し、コードの安定性を高めることができます。

「欲しいものだけを、必要な分だけ取得できる」 という点が最も大きな特徴です。

REST API 🆚 GraphQL

国の情報を含むREST APIがあると仮定しましょう。国のリストを取得するために、/countriesエンドポイントを呼び出してみましょう。

/countries

json
// GET /countries レスポンス
[
  {
    "code": "AD",
    "name": "Andorra",
    "capital": "Andorra la Vella",
    "region": "Europe"
  },
  {
    "code": "AE",
    "name": "United Arab Emirates",
    "capital": "Abu Dhabi",
    "region": "Asia"
  },
  {
    "code": "AF",
    "name": "Afghanistan",
    "capital": "Kabul",
    "region": "Asia"
  },
  // ... 200以上の国データ
]

国の固有IDコードがわかりました。今、その国についてより詳細な情報を知りたい場合は、別のAPIを呼び出す必要があります。

/countires/KR

javascript
// GET /countries/KR レスポンス
{
  "code": "KR",
  "name": "South Korea",
  "native": "대한민국",
  "phone": "82",
  "capital": "Seoul",
  "currency": "KRW",
  "languages": ["ko"],
  "emoji": "🇰🇷",
  "region": "Asia",
  "subregion": "Eastern Asia",
  "states": [
    { "name": "Seoul", "code": "11" },
    { "name": "Busan", "code": "26" },
    { "name": "Incheon", "code": "28" },
    // ... より多くの地域
  ]
}

このアプローチにはどのような問題がありますか?

  • オーバーフェッチング問題:国名と首都だけを表示する必要がある場合、他の情報を取得する必要はありません。しかし、APIを呼び出すと、すべての国のすべてのデータを受け取ります。データ量が増えるにつれて、トラフィック量も増加し、パフォーマンスの低下を招く可能性があります。
  • アンダーフェッチング問題:詳細な国情報を表示するには、2回のAPI呼び出しが必要です。したがって、国とその詳細情報を一緒に表示するには、複数のAPI呼び出しが必要です。

GraphQLによる解決

上記のREST APIの問題は、GraphQLを使用して次のように解決できます:

  • すべてのリクエストを単一のエンドポイントで処理できます。これにより、管理とメンテナンスが簡素化されます。
  • クライアントは必要なデータのみをリクエストできます。クライアントが必要なデータの構造を指定できるため、不要なデータ転送を減らし、ネットワーク効率を高めることができます。

🆚 /countries

graphql
query {
  countries {
    name
    capital
    emoji
  }
}

レスポンス

json
{
  "data": {
    "countries": [
      {
        "name": "Andorra",
        "capital": "Andorra la Vella",
        "emoji": "🇦🇩"
      },
      {
        "name": "United Arab Emirates",
        "capital": "Abu Dhabi",
        "emoji": "🇦🇪"
      },
      {
        "name": "Afghanistan",
        "capital": "Kabul",
        "emoji": "🇦🇫"
      },
      // ... より多くの国々
    ]
  }
}

🆚 /countries/KR

json
query {
  country(code: "KR") {
    name
    native
    capital
    emoji
    currency
    languages {
      name
      native
    }
    continent {
      name
    }
  }
}

レスポンス

json
{
  "data": {
    "country": {
      "name": "South Korea",
      "native": "대한민국",
      "capital": "Seoul",
      "emoji": "🇰🇷",
      "currency": "KRW",
      "languages": [
        {
          "name": "Korean",
          "native": "한국어"
        }
      ],
      "continent": {
        "name": "Asia"
      },
    }
  }
}

たった1回のリクエストで必要なすべての情報を取得しました!🫢

GraphQLの利点

1. 単一エンドポイント

GraphQLの特徴の一つは、単一のエンドポイントのみを使用することです。REST APIでは、/countries/countries/KR/countries/KR/borders/languagesなど、リソースごとに異なるエンドポイントが必要でしたが、GraphQLは通常、/graphqlという単一のURLのみを使用します。

  • フロントエンド開発の簡素化:開発者は複数のエンドポイントを覚えたり、ドキュメントを探したりする必要がなく、1つのエンドポイントだけを知っていればよいです。
  • セキュリティの強化:1つのエントリーポイントだけを保護すればよいため、認証と認可のロジックを一元化できます。

2. 必要なデータのみを正確にリクエスト可能(オーバーフェッチングの解決)

GraphQLを使用すると、クライアントは正確に必要なデータのみをリクエストできます。これによりオーバーフェッチングの問題を解決できます。

  • ネットワークトラフィックの削減:不要なデータを転送しないため、帯域幅の使用量が減少します。
  • 応答時間の改善:必要なデータのみを処理するため、サーバーの応答時間が改善される可能性があります。

3. 単一リクエストで関連データを取得(アンダーフェッチングの解決)

GraphQLを使用すると、複数のリソースのデータを1回のリクエストで取得できます。これによりアンダーフェッチングの問題を解決できます。

  • ネットワークリクエストの削減:複数のAPI呼び出しの代わりに、1回のリクエストですべてのデータを取得します。
  • ウォーターフォール方式のリクエストの排除:RESTでよく発生する順次的なAPI呼び出しが不要になります。
  • フロントエンドコードの簡素化:複数のAPI呼び出しを調整する複雑なコードが不要になります。

4. 強力な型システム

GraphQLは厳格な型システムに基づいています。各フィールドの型が明確に定義されており、APIの安定性と予測可能性が大幅に向上します。

  • コンパイル時のエラー検出:開発中に型エラーを発見できます。
  • 自動コード生成:型定義に基づいてクライアントコードを自動生成できます(TypeScriptインターフェースなど)。
  • IDE支援の向上:コード自動補完、型チェックなどの開発環境サポートが向上します。
  • 明確な契約:フロントエンドとバックエンド間に明確なデータ契約が形成されます。

GraphQLの欠点

1. 複雑なクエリ処理の負担

GraphQLはクライアントにクエリの柔軟性を提供しますが、これによりサーバー側に負担がかかる可能性があります。

  • 複雑なクエリのパフォーマンスへの影響:非常にネストされたクエリや複数のリソースをリクエストする複雑なクエリは、サーバーに大きな負荷をかける可能性があります。
typescript
query NestedQuery {
  continents {
    countries {
      languages {
        countries {
          languages {
            countries {
              ...
            }
          }
        }
      }
    }
  }
}
  • リソース攻撃の可能性:悪意のあるユーザーがサーバーリソースを消費する非常に複雑なクエリを意図的に送信する可能性があります。

2. キャッシュ実装の複雑さ

REST APIはURLベースで簡単にキャッシュできますが、GraphQLではより複雑なキャッシュ戦略が必要です。

3. エラー処理の違い

  • HTTPステータスコードの活用不足:エラーがレスポンス本文に含まれるため、処理が難しい場合があります。
  • 部分的な失敗の処理:クエリの一部だけが失敗する可能性があるため、クライアントは部分的な成功/失敗シナリオを処理する必要があります。
  • エラーデバッグ:複雑なクエリで問題が発生した場合、デバッグがより困難になる可能性があります。

4. 過度の柔軟性のリスク

GraphQLの柔軟性は、諸刃の剣となる可能性があります。

  • APIバージョン管理の混乱:フィールドを簡単に追加できるため、明示的なバージョン管理が不足する可能性があります。
  • クエリの複雑性の増加:時間の経過とともに、クライアントクエリがますます複雑になる可能性があります。
  • バックエンドロジックの露出:柔軟なクエリが内部システム構造を意図せずに露出させる可能性があります。
  • API使用パターンの理解の難しさ:様々なクライアントクエリパターンにより、最も重要なデータとフィールドを特定することが困難になる可能性があります。

GraphQLの基本タイプ

GraphQLの強力な機能の一つは型システムです。どのような型が利用可能か見てみましょう。

タイプ説明
StringUTF-8文字列
ID基本的にはStringですが、一意の識別子としての役割を示します
Int符号付き32ビット整数
Float符号付き浮動小数点値
Boolean真/偽

!(非Null)

特定のフィールドがnullを含むことができないことを示します。

graphql
const typeDefs = gql`
	type Supplies {
	    id: ID!
	    name: String!
	    price: Int
	}
`

enum (列挙型)

事前に指定された値の中から一つを返す型です。

graphql
const typeDefs = gql`
  enum Role {
      developer
      designer
      planner
  }
  enum NewOrUsed {
      new
      used
  }
`

[] (配列型)

特定の型が配列であることを示します。

graphql
const typeDefs = gql`
	type Foods {
		ingredients: String[]
	}
`

また、!の位置によって異なる意味を示すことができます。

宣言users: nullusers: [ ]users: [..., null]
[String]
[String!]
[String]!
[String!]!

union型

複数の記述された型をまとめて使用したい場合に使用します。

graphql
const typeDefs = gql`
  union Given = Equipment | Supply
`;

interface型

類似したオブジェクト型を作成するための型で、implementsのために記述されます。継承オブジェクトでインターフェースのフィールドが実装されていない場合、エラーが発生します。

typescript
interface Produce {
  id: ID!
  name: String!
  quantity: Int!
  price: Int!
}

# OK
type Fruit implements Produce {
  id: ID!
  name: String!
  quantity: Int!
  price: Int!
}

# ERROR !!
type Vegetable implements Produce {
  id: ID!
  name: String!
  quantity: Int!
}

input型

クエリやミューテーションに入るべきパラメータの型を指定できます。

graphql
input PostPersonInput {
    first_name: String!
    last_name: String!
}

type Mutation {
    postPerson(input: PostPersonInput): People!
}

いつGraphQLを選ぶべきか?

GraphQLは、より柔軟で効率的なAPIを作成するための強力なツールです。REST APIは多くの状況で依然として良い選択ですが、複雑なデータ要件と様々なクライアントをサポートする必要がある状況では、GraphQLは明確な利点を提供できます。

**しかし、GraphQLは万能の解決策ではなく、特定の状況に適したツールであることを覚えておいてください。**プロジェクトの要件をよく分析し、REST APIとGraphQLの間で適切に選択することが重要です。

以下のような状況では、GraphQLが良い選択かもしれません:

  1. 複雑なデータ関係を持つアプリケーション
    • 複数のエンティティ間に複雑な関係がある国情報アプリのような場合
    • ユーザー、投稿、コメント、いいねなど様々な関係を持つソーシャルネットワーク
  2. 様々なクライアントをサポートする必要がある場合
    • ウェブ、モバイルアプリ、デスクトップなど様々なプラットフォームで異なるデータ要件がある場合
    • 各クライアントが同じバックエンドからカスタマイズされたデータを取得する必要がある場合
  3. 迅速な製品反復とフロントエンド開発速度が重要な場合
    • バックエンドの変更なしにフロントエンドの要件を簡単に対応する必要がある場合
    • ユーザーインターフェースと機能が頻繁に変更されるスタートアップやアジャイル環境
  4. マイクロサービス統合が必要な場合
    • 複数のマイクロサービスのデータを一つの一貫したAPIに統合する必要がある場合
    • BFF(Backend For Frontend)パターンを実装する場合

RESTが依然として適切な状況

以下のような状況では、REST APIがより適切かもしれません:

  1. 単純なCRUD操作が主である場合
    • 関係が少なく単純なデータ構造を持つアプリケーション
    • データリクエストパターンが一貫していて予測可能な場合
  2. HTTPキャッシングが重要な場合
    • 公開APIのように効率的なHTTPキャッシングが重要な状況
    • CDNを通じたキャッシングが重要なパフォーマンス要素である場合
  3. ファイルアップロードが主要機能である場合
    • 大容量ファイル処理とストリーミングが主要要件である場合
  4. チームの学習曲線を最小限に抑える必要がある場合
    • チームがRESTに慣れていて、新しいパラダイムを学ぶ時間が限られている場合

まとめ

すべての技術と同様に、GraphQLも特定のトレードオフを伴います。最終的に最も重要なのは、ユーザーとビジネスの要件を満たすことです。GraphQLであれRESTであれ、あるいは両方を組み合わせたアプローチであれ、プロジェクトの具体的な状況と目標に合った技術を選びましょう!


参考

❤️ 0
🔥 0
😎 0
⭐️ 0
🆒 0