1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

本記事は、自分の備忘録も兼ねて書いてます。

GraphQLとは

GraphQLとは「APIのためのクエリ言語」です。

言い換えると、「Webサーバー(API)からデータを取得するための、非常に柔軟で効率のよい"問い合わせ方法"のルール」です。

Facebook(現Meta社)によって開発が始まり、オープンソースとして公開されました。

従来のAPI(REST)の課題

アプリケーション(特にスマートフォンアプリ)が複雑化するにつれ、従来のREST APIでは主に2つの課題が顕著になってきました。

  1. データの取得しすぎ(オーバーフェッチ)
    • クライアント(アプリ)は「名前」だけ欲しいのに、APIが「名前、住所、電話番号、生年月日…」といった不要なデータまで返してしまう問題。
  2. データの取得不足(アンダーフェッチ)
    • クライアントが「記事本文」と「その記事へのコメント一覧」が欲しい場合、/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)

api.py
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フィールドだけが必要だったが、サーバーは設計(実装)通りにaddressemailなど、不要なデータもすべて送信(オーバーフェッチ)

アンダーフェッチの例

要求: 「記事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 と、その記事に関連する commentstext を、一度にまとめて返して」と、ネスト(入れ子構造)で指定。

# GraphQLのクエリ(イメージ)
query {
  post(id: "p1") {
    title
    comments {
      text
    }
  }
}

# サーバーからの応答(イメージ)
# { "data": { "post": { "title": "GraphQL入門", "comments": [ ... ] } } }

→ 1回のリクエストで、必要なデータが揃う


まとめ

  • GraphQLは「APIのためのクエリ言語」である
  • REST APIには、固定的なエンドポイント設計に起因する課題があった
  • GraphQLは、クライアントが「欲しいものだけ」を「一度に」要求する仕組みによって、これらの課題を解決しようとしている

次回は、オーバーフェッチに焦点を当て、比較・解説しようと思います。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?