0
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?

【FastAPI入門 #3】パスパラメータ・クエリパラメータ・リクエストボディの使い分け

0
Posted at

はじめに

前回の記事では、Pydanticを使った型バリデーションを解説しました。

FastAPIでAPIを作っていると、「データをどこで受け取るか」という場面に必ず直面します。

GET  /users/42          ← 42 はどこ?
GET  /users?page=2      ← page=2 はどこ?
POST /users  + JSON本文  ← JSONはどこ?

この3つは 受け取る場所が違う ため、名前も使い方も異なります。

名前 場所 用途
パスパラメータ URLのパス部分 /users/{id} リソースの特定
クエリパラメータ URLの ? 以降 ?page=2 絞り込み・ページング
リクエストボディ HTTPリクエストの本文(JSON) データの作成・更新

この記事では、それぞれの仕組みと使い分けの判断基準をコードと図で整理します。


1. パスパラメータ

基本の書き方

URLのパスに {変数名} を埋め込み、関数の引数に同じ名前を書くだけです。

from fastapi import FastAPI

app = FastAPI()

@app.get("/users/{user_id}")
def get_user(user_id: int):
    return {"user_id": user_id}
  • GET /users/42user_id = 42
  • GET /users/99user_id = 99

型ヒントに int を書くだけで、FastAPIが自動で整数に変換します。
/users/abc のように整数に変換できない値が来た場合は 422 エラーになります。

複数のパスパラメータ

@app.get("/users/{user_id}/posts/{post_id}")
def get_user_post(user_id: int, post_id: int):
    return {"user_id": user_id, "post_id": post_id}
  • GET /users/1/posts/5user_id=1, post_id=5

注意:パスの順序

# ✖ 問題のある書き方
@app.get("/users/{user_id}")
def get_user(user_id: int): ...

@app.get("/users/me")       # ← "me" が {user_id} に吸い込まれる!
def get_current_user(): ...
# 〇 固定パスを先に書く
@app.get("/users/me")       # ← 先に定義
def get_current_user(): ...

@app.get("/users/{user_id}")
def get_user(user_id: int): ...

FastAPIはルートを上から順に評価するため、固定パスは動的パスより先に定義する必要があります。


2. クエリパラメータ

基本の書き方

URLパスに含まれない引数は、FastAPIが自動でクエリパラメータとして扱います。

@app.get("/users")
def get_users(page: int = 1, per_page: int = 10):
    return {"page": page, "per_page": per_page}
  • GET /userspage=1, per_page=10(デフォルト値)
  • GET /users?page=3page=3, per_page=10
  • GET /users?page=2&per_page=5page=2, per_page=5

任意パラメータ(Optional)

from typing import Optional

@app.get("/users")
def get_users(
    page: int = 1,
    per_page: int = 10,
    keyword: Optional[str] = None   # 省略可能
):
    result = {"page": page, "per_page": per_page}
    if keyword:
        result["keyword"] = keyword
    return result
  • GET /users → keyword なし
  • GET /users?keyword=Alicekeyword="Alice" で絞り込み

パスパラメータとクエリパラメータの組み合わせ

@app.get("/users/{user_id}/posts")
def get_user_posts(
    user_id: int,                    # パスパラメータ
    page: int = 1,                   # クエリパラメータ
    published_only: bool = False     # クエリパラメータ(bool型も使える)
):
    return {
        "user_id": user_id,
        "page": page,
        "published_only": published_only
    }
  • GET /users/1/posts?page=2&published_only=true
    user_id=1, page=2, published_only=True

💡 boolのクエリパラメータ
FastAPIは true / false / 1 / 0 / on / off などを自動でboolに変換してくれます。


3. リクエストボディ

基本の書き方

Pydanticモデルを引数の型ヒントに書くと、FastAPIは自動でリクエストボディから受け取ります。

from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class UserCreate(BaseModel):
    name: str
    age: int
    email: str

@app.post("/users")
def create_user(user: UserCreate):
    return {"message": f"{user.name} を作成しました", "data": user}

リクエスト時にはJSONをボディに含めます:

POST /users
Content-Type: application/json

{
  "name": "Alice",
  "age": 30,
  "email": "alice@example.com"
}

3つを同時に使う

3種類のパラメータは同時に使えます。FastAPIは引数の型から自動で判断します。

from fastapi import FastAPI
from pydantic import BaseModel
from typing import Optional

app = FastAPI()

class PostCreate(BaseModel):
    title: str
    content: str

@app.post("/users/{user_id}/posts")
def create_post(
    user_id: int,                       # パスパラメータ(URLに含まれる)
    draft: bool = False,                # クエリパラメータ(?draft=true)
    post: PostCreate = None             # リクエストボディ(JSON本文)
):
    return {
        "user_id": user_id,
        "draft": draft,
        "post": post
    }

FastAPIの判定ルール:

URLパスに {変数名} がある  → パスパラメータ
Pydanticモデルの型        → リクエストボディ
それ以外                  → クエリパラメータ

4. 使い分けの判断基準

「どれを使えばいいか」迷ったときの判断フローです。

送りたいデータは何?
│
├─ 「どのリソースか」を特定する ID や名前
│     → パスパラメータ  /users/{id}
│
├─ 「条件を絞る」ための補助情報(ページ・フィルター・並び順など)
│     → クエリパラメータ  ?page=2&sort=name
│
└─ 「作成・更新」のための詳細データ(名前・住所・本文など)
      → リクエストボディ  { "name": "...", "age": 30 }

具体例で確認

# 記事一覧(絞り込みあり)
GET /articles?category=tech&page=1&per_page=20
# → category, page, per_page は全部クエリパラメータ

# 記事1件取得
GET /articles/123
# → 123 はパスパラメータ

# 記事作成
POST /articles
Body: { "title": "...", "content": "...", "category": "tech" }
# → Body全体はリクエストボディ

# 記事更新
PUT /articles/123
Body: { "title": "新しいタイトル" }
# → 123 はパスパラメータ、Bodyはリクエストボディ

5. よくある間違いと対処法

間違い1:GETでリクエストボディを使う

# ✖ GETにボディは使えない(HTTP仕様的にNG)
@app.get("/users")
def get_users(filter: UserFilter):  # Pydanticモデルをつけてしまっている
    ...

# 〇 GETの絞り込みはクエリパラメータで
@app.get("/users")
def get_users(name: Optional[str] = None, age: Optional[int] = None):
    ...

間違い2:パスパラメータの型を書き忘れる

# ✖ 型なし → 文字列として受け取ってしまう
@app.get("/users/{user_id}")
def get_user(user_id):   # 型ヒントなし
    return user_id       # "42" (文字列)

# 〇 型を書く → 自動変換・バリデーションが効く
@app.get("/users/{user_id}")
def get_user(user_id: int):
    return user_id       # 42 (整数)

間違い3:クエリパラメータにデフォルト値を忘れる

# ✖ デフォルト値なし → 必須パラメータになる
@app.get("/users")
def get_users(page: int):   # デフォルト値なし
    ...
# GET /users だけでアクセスすると 422 エラー

# 〇 デフォルト値を設定
@app.get("/users")
def get_users(page: int = 1):
    ...

6. まとめ:FastAPIの自動判定ルール

@app.post("/users/{user_id}/posts")
def create_post(
    user_id: int,          # ① URLパスに {user_id} → パスパラメータ
    draft: bool = False,   # ② Pydanticモデルでない + デフォルト値あり → クエリパラメータ
    post: PostCreate,      # ③ Pydanticモデル → リクエストボディ
):
    ...

FastAPIが引数を見て自動でルーティングしてくれるため、余計な設定が不要です。この判定ルールを覚えるだけで、ほとんどのAPIを迷わず実装できるようになります。

次回は FastAPI + SQLiteでCRUDアプリを作る を解説します。今回までの知識をベースに、実際にデータベースと繋いで「ユーザー管理API」を作り切ります。


参考


この記事は学習日記として書いています。間違いや補足があればコメントいただけると嬉しいです!

0
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
0
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?