TL;DR

Meta が開発した、Web API のためのクエリ言語。GraphQL Schema で型を定義し、クライアントは単一のエンドポイントに必要なデータだけを問い合わせる。

なぜ必要か

REST API と比べて、速度、柔軟性、開発のしやすさを重視して設計されている。クライアントが必要なデータを指定するので、Over-fetching や Under-fetching を避けられる。モバイル端末の普及で、限られた帯域で効率よく取得したいというニーズが増えたことが背景にある。

また、各 UI コンポーネントが Fragment で必要なデータを宣言し、1 つのクエリにまとめて送れる(Fragment Colocation)。

仕組み

スキーマを独自のスキーマ定義言語(SDL)で書き、独自のクエリ言語でリクエストする。特殊な通信プロトコルは必要とせず、この点が gRPC と大きく違う。リクエストには Query(取得)、Mutation(書き換え)、Subscription(購読)がある。

型システム

スキーマの定義は必須で、型に違反したリクエストやレスポンスはランタイムエラーになる。そのため、スキーマを API ドキュメントとして使え、実装とずれにくい。GraphiQL ではスキーマを使って補完できる。

クエリの機能

  • Fragment: クエリの重複部分を切り出して再利用し、DRY にする。
  • 変数: $ で始まる変数で外から値を受け取る。変数ごとに型を指定する。
  • ディレクティブ: @ で始まる修飾子。スキーマでは @deprecated で廃止予定を示し、クエリでは @include と @skip で取得するフィールドを条件で切り替える。

例

fragment PostItem on Post {
  title
  author {
    name
  }
}
 
query IncludeQuery($showRanking: Boolean!) {
  post {
    ...PostItem
    ranking @include(if: $showRanking)
  }
}
type Post {
  title: String! @deprecated
  body: String! @deprecated(reason: "Use `content` instead.")
  content: String! @constraint(maxLength: 1000)
}

注意点

クライアントが自由にクエリを組めるので、サーバの負荷を予測しにくい。負荷への対策は必須である。

REST API と違ってキャッシュが複雑になりやすく、学習コストもかかる。@include や @skip はクエリの認知的負荷を上げるので、困ったときの手段と考えるのがよい。

自分の理解

スキーマの定義が必須とはいえ、Code first のアプローチではスキーマを手で書かなくてよい。

当初は N+1 問題が起きやすいことをデメリットに挙げていたが、取り消した。関連する話は GraphQL における N+1 問題の発生しやすさはデメリットなのか にある。

関連

出典