はじめに
本記事は、自分の備忘録も兼ねて書いてます。
第1回は、GraphQLの概要とREST APIの課題について記載しました。
今回は、APIの課題の1つ「オーバーフェッチ (Over-fetching)」について、なぜ問題となるのか、GraphQLがどのように解決するのかを見ていきます。
【動作環境】
本記事は、第1回で作成したapi.py(Flaskサーバー)が、以下の環境で起動していることを前提とします。
-
サーバー:
python api.pyを実行し、http://127.0.0.1:5000/で起動中 -
クライアント:
curlコマンドが実行可能なターミナル
「オーバーフェッチ」について
まず、「オーバーフェッチ」とは何か。
これは、APIにデータを要求した際に、クライアント(アプリケーション)が「実際に必要としている情報よりも多くの情報」を受け取ってしまうことです。
1. デモ
以下は、第1回で実行したコマンドです。
- クライアントの要求: 「IDが1のユーザーの名前(name)だけ」が欲しい
- 実行コマンド:
$ curl http://127.0.0.1:5000/users/1 - APIからのレスポンス:
{ "address": "東京都...", "email": "taro@example.com", "id": "1", "joined_date": "2023-01-01", "name": "佐藤 太郎" }
本来、クライアントが欲しかったのは "name": "佐藤 太郎" という情報だけです。
しかし、実際にはaddress、email、joined_dateといった、不要なデータが含まれてしまっています。(オーバーフェッチ)
2. なぜオーバーフェッチが問題なのか
「データの取りすぎ」は、以下のような問題があります。
-
クライアント側の問題:
不要なデータもネットワークを通じて送受信されるため、通信データ量が増加します。これは、アプリケーションの応答速度低下に繋がります。 -
サーバー側の問題:
サーバー側も、不要なデータもデータベースから取得し、JSON形式に変換する処理を行っています。これは、データベースやサーバーのCPUリソースの無駄遣いにつながります。
原因分析
この問題は、api.py のコード設計に起因しています。
api.py の /users/<user_id> エンドポイント:
@app.route('/users/<user_id>', methods=['GET'])
def get_user(user_id):
user = users_data.get(user_id)
if user:
# 常に「user」オブジェクトの「すべて」を返している。
return jsonify(user)
else:
return jsonify({"error": "User not found"}), 404
この関数は、「user_id に紐づくユーザー情報をすべて返す」ことしかできません。
そのため、クライアント側の「トップページでは名前だけ欲しい」「設定画面では名前とEmailが欲しい」といった、細かい対応ができない設計になっています。
従来のREST APIでの対策
この問題をREST APIで解決しようとすると、管理が難しくなるという問題が発生します。
- 対策1:専用のエンドポイントを新設
以下のように、「名前だけ返すAPI」を作ったとします。
例)@app.route('/users/<user_id>/name')
しかし、「Emailだけ欲しい」「名前とEmailが欲しい」など、クライアントの要求の組み合わせの数だけ、APIのエンドポイント(URL)が増加し、管理が非常に困難になります。
- 対策2:クエリパラメータで指定する
GET /users/1?fields=name,email のように、URLの末尾で欲しいフィールドを指定できるようにします。
この場合、get_user 関数の実装が非常に複雑(fieldsパラメータを解析し、userオブジェクトから動的にキーを抜き出す等)になります。これは、REST APIのシンプルな設計思想から外れていきます。
GraphQLによる解決策
GraphQLは、この「クライアントが必要なフィールドだけを指定する」という技術仕様を持っています。
クライアントは、サーバーが固定したURLにアクセスするのではなく、サーバーに対して「このような構造のデータが欲しい」という「クエリ」を送信します。
# クライアントが欲しいデータだけを指定して送信
query {
user(id: "1") {
name
}
}
GraphQLサーバーは、このクエリを受け取ると、user の id: "1" のデータのうち、nameフィールドだけを解決する処理(リゾルバ)を実行します。addressやemailのリゾルバは呼び出されません。
その結果、サーバーからの応答は、クライアントが要求した通りの最小限のデータになります。
{
"data": {
"user": {
"name": "佐藤 太郎"
}
}
}
このように、GraphQLはクライアントが必要なフィールドを「宣言」する仕組みによって、オーバーフェッチの問題を解決します。
まとめ
- オーバーフェッチは、クライアントが必要としないデータまで取得してしまうことで、通信量の増大やリソースの無駄遣いにつながる
- REST APIでは、サーバー側が返すデータ構造を固定する設計のため、構造的に、オーバーフェッチが発生しやすい
- GraphQLは、クライアントが欲しいフィールドを「クエリ」として明示的に宣言することで、この問題を解決する
次回は、「アンダーフェッチ」(データが足りない問題)について記載します。