はじめに
本記事は、自分の備忘録も兼ねて書いてます。
GraphQLとは
GraphQLとは「APIのためのクエリ言語」です。
言い換えると、「Webサーバー(API)からデータを取得するための、非常に柔軟で効率のよい"問い合わせ方法"のルール」です。
Facebook(現Meta社)によって開発が始まり、オープンソースとして公開されました。
従来のAPI(REST)の課題
アプリケーション(特にスマートフォンアプリ)が複雑化するにつれ、従来のREST APIでは主に2つの課題が顕著になってきました。
-
データの取得しすぎ(オーバーフェッチ)
- クライアント(アプリ)は「名前」だけ欲しいのに、APIが「名前、住所、電話番号、生年月日…」といった不要なデータまで返してしまう問題。
-
データの取得不足(アンダーフェッチ)
- クライアントが「記事本文」と「その記事へのコメント一覧」が欲しい場合、
/posts/1(記事用API)と/posts/1/comments(コメント用API)のように、複数のAPIを何度も呼び出す必要がある問題。
- クライアントが「記事本文」と「その記事へのコメント一覧」が欲しい場合、
文章だけでは分かりにくいため、これらの課題をコードで確認してみましょう。
【動作環境】
従来のAPI(REST)のサンプルコードを動作させるための環境を準備します。
- **Python: 3.x
- Flask: Pythonで簡易的なWebサーバー(API)を構築
- curl: コマンドラインツール(Windows, macOS, Linuxのいずれでも利用可)
まず、Flaskをインストールします。ターミナルで以下のコマンドを実行してください。
pip install Flask
従来のREST APIの実装と実行
以下は、2つの課題(オーバーフェッチ、アンダーフェッチ)を含んだ、ブログのAPIです。
1. API (Python/Flask)
from flask import Flask, jsonify
app = Flask(__name__)
# --- サンプルデータ ---
# ユーザー情報(オーバーフェッチの原因になりやすい)
users_data = {
"1": {
"id": "1",
"name": "佐藤 太郎",
"email": "taro@example.com",
"address": "東京都...",
"joined_date": "2023-01-01"
},
"2": {
"id": "2",
"name": "鈴木 花子",
"email": "hanako@example.com",
"address": "大阪府...",
"joined_date": "2023-03-15"
},
}
# 記事情報
posts_data = {
"p1": {"id": "p1", "author_id": "1", "title": "GraphQL入門", "body": "GraphQLについて解説します。"},
"p2": {"id": "p2", "author_id": "2", "title": "Flaskの使い方", "body": "Flaskは軽量なフレームワークです。"},
}
# コメント情報(アンダーフェッチの原因になりやすい)
comments_data = {
"p1": [ # 記事p1へのコメント
{"id": "c1", "text": "分かりやすいです。"},
{"id": "c2", "text": "勉強になります。"}
],
"p2": [ # 記事p2へのコメント
{"id": "c3", "text": "試してみます。"}
]
}
# -------------------------------------------------
# (1) 特定のユーザー情報を返すエンドポイント
@app.route('/users/<user_id>', methods=['GET'])
def get_user(user_id):
user = users_data.get(user_id)
if user:
# 常にユーザーの「全情報」を返す
return jsonify(user)
else:
return jsonify({"error": "User not found"}), 404
# (2) 特定の記事情報を返すエンドポイント
@app.route('/posts/<post_id>', methods=['GET'])
def get_post(post_id):
post = posts_data.get(post_id)
if post:
return jsonify(post)
else:
return jsonify({"error": "Post not found"}), 404
# (3) 特定の記事のコメント一覧を返すエンドポイント
@app.route('/posts/<post_id>/comments', methods=['GET'])
def get_comments(post_id):
comments = comments_data.get(post_id)
if comments is not None:
return jsonify(comments)
else:
# 記事が存在しない、またはコメントが0件の場合
return jsonify([])
if __name__ == '__main__':
app.run(debug=True, port=5000)
2. APIの起動
api.py を保存したディレクトリで以下のコマンドを実行し、APIサーバーを起動します。
python api.py
* Running on http://127.0.0.1:5000/ のような表示が出れば成功です。
curlによるアクセス
別のターミナルを開き、curlコマンドを使って、起動したAPIサーバーにアクセスします。
オーバーフェッチの例
要求: 「IDが1のユーザーの名前(name)だけ」が欲しい
実行: /users/1 にアクセス
$ curl http://127.0.0.1:5000/users/1
結果:
{
"address": "東京都...",
"email": "taro@example.com",
"id": "1",
"joined_date": "2023-01-01",
"name": "佐藤 太郎"
}
→ クライアントはnameフィールドだけが必要だったが、サーバーは設計(実装)通りにaddressやemailなど、不要なデータもすべて送信(オーバーフェッチ)
アンダーフェッチの例
要求: 「記事IDがp1の記事タイトル(title)と、その記事のコメント一覧(comments)」が欲しい
実行(1): /posts/p1 エンドポイントにアクセスし、記事タイトルを取得
$ curl http://127.0.0.1:5000/posts/p1
結果(1):
{
"author_id": "1",
"body": "GraphQLについて解説します。",
"id": "p1",
"title": "GraphQL入門"
}
→ これだけではコメント一覧が足りない(アンダーフェッチ)
実行(2): コメント一覧を取得するために、2回目のリクエストを /posts/p1/comments に送信
$ curl http://127.0.0.1:5000/posts/p1/comments
結果(2):
[
{
"id": "c1",
"text": "分かりやすいです。"
},
{
"id": "c2",
"text": "勉強になります。"
}
]
→ 欲しい情報を揃えるために、合計2回のリクエストが必要になった。(アンダーフェッチによるリクエスト回数の増加)
GraphQLの「思想」
GraphQLは、これらの課題を「クライアント(アプリ)側が、欲しいデータの構造を"宣言"する」という思想で解決します。
サーバーは「/users」「/posts」のような固定のエンドポイントを多数用意する代わりに、原則として「/graphql」という単一のエンドポイントを持ちます。
クライアントは、その単一のエンドポイントに対し、以下のような「クエリ(問い合わせ文)」を送信します。
(参考)GraphQLの場合
※これらはGraphQLのクエリのイメージを示すコードです
課題(1) オーバーフェッチの解決イメージ:
「ID 1 の ユーザーから、name フィールドだけ欲しい」と、クライアントが明示的に指定。
# GraphQLのクエリ(イメージ)
query {
user(id: "1") {
name
}
}
# サーバーからの応答(イメージ)
# { "data": { "user": { "name": "佐藤 太郎" } } }
課題(2) アンダーフェッチの解決イメージ:
「ID p1 の 記事の title と、その記事に関連する comments の text を、一度にまとめて返して」と、ネスト(入れ子構造)で指定。
# GraphQLのクエリ(イメージ)
query {
post(id: "p1") {
title
comments {
text
}
}
}
# サーバーからの応答(イメージ)
# { "data": { "post": { "title": "GraphQL入門", "comments": [ ... ] } } }
→ 1回のリクエストで、必要なデータが揃う
まとめ
- GraphQLは「APIのためのクエリ言語」である
- REST APIには、固定的なエンドポイント設計に起因する課題があった
- GraphQLは、クライアントが「欲しいものだけ」を「一度に」要求する仕組みによって、これらの課題を解決しようとしている
次回は、オーバーフェッチに焦点を当て、比較・解説しようと思います。