"パスとクエリ、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 つの基本操作を表します:
- Create (作成): 新しいデータの追加
- Read (読み取り): データの取得や閲覧
- Update (更新): 既存データの変更
- 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 は関数パラメータの定義に基づいて、自動的に以下のように区別します:
- パスパラメータ:
- URL パス内に中括弧
{}
で囲まれたパラメータ - パス定義内に宣言されているパラメータ
- 例:
/items/{item_id}
のitem_id
- URL パス内に中括弧
- クエリパラメータ:
- 基本型(int, float, str, bool など)のパラメータ
- 通常、デフォルト値を持つ(
None
や他の値) - 例:
q: str = None
やskip: int = 0
- リクエストボディ:
- 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 はそれぞれを正しく処理します。パラメータを特定の順序で宣言する必要もなく、名前によって検出されます。