ホーム

"パスとクエリ、CURD"の関係性、シンプルな例

パスパラメータ、クエリパラメータと CRUD 操作の間には密接な関係があり、Web アプリケーションや API の設計において重要な概念です。

パスパラメータとクエリパラメータの基本

パスパラメータ:

  • URL のパスの一部として組み込まれる
  • 特定のリソースを識別するために使用される
  • 例: https://example.com/api/users/123 (123 はユーザー ID)
  • 一般的に必須のパラメータに使用される

クエリパラメータ:

  • URL の末尾に?に続けて追加される
  • フィルタリング、ソート、検索条件に使用される
  • 例: /products?category=12&color=34&size=m
  • 一般的に任意のパラメータに使用される

リクエストボディの基本

リクエストボディ:

  • HTTP リクエストの後半部分にあるデータ本体を格納した領域
  • 主に POST、PUT、PATCH などのメソッドで使用される
  • GET リクエストでは通常空であることが多い
  • クライアントからサーバーにデータを送信するために利用される

リクエストボディの特徴:

  • 大量または複雑なデータを送信する場合に使用
  • JSON や XML、フォームデータなどの形式でデータを送信可能
  • GET リクエストとは違い、URL に表示されないため機密データの送信に適している
  • ボディ部が空でない場合は、Content-Type ヘッダでデータの MIME タイプを指定
  • Content-Length ヘッダでボディ部のデータ長をバイト単位で指定

リクエストボディの用途:

  • フォームデータの送信
  • ファイルのアップロード
  • リソースの作成または更新のためのデータ送信
  • API リクエストでのパラメータやクエリ情報の送信

リクエストボディの例:

POST /users
Content-Type: application/json
Content-Length: 45

{
  "name": "Tanaka",
  "age": 30,
  "email": "tanaka@example.com"
}

この例では、新しいユーザーを作成するための JSON データをリクエストボディに含めています。

CRUD 操作の基本

CRUD は以下の 4 つの基本操作を表します:

  1. Create (作成): 新しいデータの追加
  2. Read (読み取り): データの取得や閲覧
  3. Update (更新): 既存データの変更
  4. Delete (削除): データの削除

パスパラメータ、クエリパラメータと CRUD の関係性

Create 操作の例

POST /users
  • リクエストボディにユーザー情報を含める
  • パスパラメータは通常使用しない(新規リソース作成のため)

Read 操作の例

単一リソースの取得:

GET /users/123
  • パスパラメータ(123)で特定のユーザーを識別

複数リソースの取得・フィルタリング:

GET /users?age=30&status=active
  • クエリパラメータでフィルタリング条件を指定

Update 操作の例

PUT /users/123
  • パスパラメータ(123)で更新対象のユーザーを識別
  • リクエストボディに更新内容を含める

Delete 操作の例

DELETE /users/123
  • パスパラメータ(123)で削除対象のユーザーを識別

使い分けの基準

  • パスパラメータ:
    • リソースを一意に特定する必須パラメータに使用
    • 例: ユーザー ID、商品 ID
  • クエリパラメータ:
    • 検索条件や任意のパラメータに使用
    • 例: ソート順、ページ番号、フィルター条件
  • リクエストボディ:
    • 大量または複雑なデータを送信する場合
    • 主に POST、PUT、PATCH リクエストで使用

FastAPI を使用した実装イメージ

@app.get("/items/{item_id}")  # パスパラメータ
async def read_item(item_id: int):
    return {"item_id": item_id}

@app.get("/items")  # クエリパラメータ
async def read_items(skip: int = 0, limit: int = 10):
    return {"skip": skip, "limit": limit}

@app.post("/items")  # Create操作
async def create_item(item: Item):
    return item

@app.put("/items/{item_id}")  # Update操作
async def update_item(item_id: int, item: Item):
    return {"item_id": item_id, **item.dict()}

@app.delete("/items/{item_id}")  # Delete操作
async def delete_item(item_id: int):
    return {"message": "Item deleted"}

これらの例から、リソースを特定する必要がある操作(Read 単一、Update、Delete)ではパスパラメータが使われ、フィルタリングや条件指定が必要な操作(Read 複数)ではクエリパラメータが使われることがわかります

HTTP メソッド別の各パラメータの使い方

HTTP メソッド(GET, POST, PUT, DELETE)ごとにパスパラメータ、クエリパラメータ、リクエストボディの一般的な使い方と組み合わせを整理します。

GET メソッドの場合

パスパラメータ: ✅ 使用する

  • 特定のリソースを識別するために利用される
  • 例: /items/123 (123 はアイテム ID)

クエリパラメータ: ✅ 使用する

  • フィルタリング、ソート、ページングなどに使用される
  • 例: /items?name=hoge(検索)
  • 例: /items?sort_by=price&order=asc(ソート)
  • 例: /items?page=3&limit=50(ページング)

リクエストボディ: ❌ 基本的に使用しない

  • HTTP 仕様上はリクエストボディを持つことができるが、一般的には使用しない
  • GET の場合はパスパラメータやクエリパラメータを使用すべき

POST メソッドの場合

パスパラメータ: ✅ 使用する

  • リソースのコレクションを指定するのに使用
  • 例: /items (アイテムコレクションに新規追加)

クエリパラメータ: ⚠️ 補助的に使用

  • 主要ではないが補足情報がある場合に使用可能
  • 例: /items?draft=true (下書きとして作成)

リクエストボディ: ✅ 使用する

  • 作成するリソースのデータを含める
  • 例: POST /items に JSON でアイテム情報を含める
  • 複雑なデータ構造の送信に適している

PUT メソッドの場合

パスパラメータ: ✅ 使用する

  • 更新対象の特定リソースを識別する
  • 例: /items/123 (123 はアイテム ID)

クエリパラメータ: ⚠️ 補助的に使用

  • 主要ではないが補足情報がある場合に使用可能
  • 例: /items/123?version=2 (バージョン指定)

リクエストボディ: ✅ 使用する

  • 更新するリソースの情報を含める
  • 例: PUT /items/123 に JSON でアイテム情報を含める
  • リソースを一意に識別する情報はボディではなくパスパラメータに含めるべき

DELETE メソッドの場合

パスパラメータ: ✅ 使用する

  • 削除対象の特定リソースを識別する
  • 例: /items/123 (123 はアイテム ID)

クエリパラメータ: ⚠️ 補助的に使用

  • 主要ではないが補足情報がある場合に使用可能
  • 例: /items/123?force=true (強制削除の指定)

リクエストボディ: ❌ 基本的に使用しない

  • 一般的にはリクエストボディは使用しない
  • 複雑な削除条件がある特殊なケースでは使用することもある

FastAPI での実装イメージ

@app.get("/items/{item_id}")
async def read_item(item_id: int, q: str = None):
    # パスパラメータ: item_id
    # クエリパラメータ: q
    result = {"item_id": item_id}
    if q:
        result.update({"q": q})
    return result

@app.post("/items")
async def create_item(item: Item):
    # リクエストボディ: item
    return item

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, q: str = None):
    # パスパラメータ: item_id
    # リクエストボディ: item
    # クエリパラメータ: q (任意)
    result = {"item_id": item_id, **item.dict()}
    if q:
        result.update({"q": q})
    return result

@app.delete("/items/{item_id}")
async def delete_item(item_id: int):
    # パスパラメータ: item_id
    return {"message": "Item deleted"}

これらの組み合わせにより、RESTful API の設計原則に沿った効果的なエンドポイントを設計できます。 FastAPI では、パスパラメータ、リクエストボディ、クエリパラメータを明確に区別する仕組みがあります。その見分け方は主に関数パラメータの定義方法によって決まります。

FastAPI でのパラメータの区別方法

FastAPI は関数パラメータの定義に基づいて、自動的に以下のように区別します:

  1. パスパラメータ:
    • URL パス内に中括弧 {} で囲まれたパラメータ
    • パス定義内に宣言されているパラメータ
    • 例: /items/{item_id}item_id
  2. クエリパラメータ:
    • 基本型(int, float, str, bool など)のパラメータ
    • 通常、デフォルト値を持つ(None や他の値)
    • 例: q: str = Noneskip: int = 0
  3. リクエストボディ:
    • Pydantic モデル型として宣言されたパラメータ
    • 例: item: Item (ここで Item は Pydantic モデル)

実装イメージ

以下の例で FastAPI がどのようにパラメータを区別しているかを見てみましょう:

from fastapi import FastAPI
from pydantic import BaseModel

class Item(BaseModel):
    name: str
    description: str | None = None
    price: float
    tax: float | None = None

app = FastAPI()

@app.put("/items/{item_id}")
async def update_item(item_id: int, item: Item, q: str | None = None):
    result = {"item_id": item_id, **item.dict()}
    if q:
        result.update({"q": q})
    return result

この例では:

  • item_id は URL パスに含まれているためパスパラメータとして認識される
  • item は Pydantic モデル型なのでリクエストボディとして認識される
  • q は単一の型(str)で、デフォルト値(None)があるためクエリパラメータとして認識される

FastAPI はこれらのパラメータを自動的に区別し、適切な場所からデータを取得します。つまり、パスパラメータは URL パスから、クエリパラメータはクエリ文字列から、そしてリクエストボディは HTTP リクエストのボディ部分から取得されます。

異なるパラメータ型を組み合わせることも可能であり、FastAPI はそれぞれを正しく処理します。パラメータを特定の順序で宣言する必要もなく、名前によって検出されます。