
STEP 2では、GET APIで値を受け取るためのパスパラメータとクエリパラメータを学びます。
URLと関数の引数がどのように結び付くのかを確認し、実際にAPIを作成します。
また、型指定によるデータ変換やバリデーション、必須・任意パラメータの違いも確認します。

それではSTEP2を始めましょう!
今回学ぶこと
今回のステップで学ぶ内容を一覧で出してもらいました。

STEP2では以下の内容を学習します。
- GET APIで扱うパラメータの全体像を理解する
- パスパラメータを使うGET APIを実装・動作確認する
- 型指定によるデータ変換とバリデーションを確認する
- クエリパラメータを使うGET APIを実装・動作確認する
- クエリパラメータの必須・任意・デフォルト値を理解する
- パスパラメータとクエリパラメータを組み合わせる
- Swagger UIでパラメータのAPI仕様を確認する
- 自力課題
学習開始
それでは学習をはじめます。
GET APIで扱うパラメータの全体像を理解する

まずはパスパラメータとクエリパラメータについて理解します。
STEP 1では、固定URLのGET APIを作りました。
@app.get("/")
def read_root():
return {"message": "Hello World"}
この場合、アクセスするURLは常に / で、クライアントから特別な値を受け取っていません。
実際のAPIでは、/users/10のように「IDが10のユーザーを取得したい」こともあれば、/users?limit=20のように「最大20件取得したい」と指定することもあります。
ここで登場するのが、今回の主役である パスパラメータ と クエリパラメータ です。
| 種類 | URL例 | 主な用途 |
|---|---|---|
| パスパラメータ | /users/10 | 特定のリソースを指定する |
| クエリパラメータ | /users?limit=20 | 検索・絞り込み・並び替えなどの条件を指定する |

FastAPIで特に押さえておきたいところがFastAPIではどう区別するのかです。
たとえば、次のようなGET APIのコードがあります。
@app.get("/users/{user_id}")
def get_user(user_id: int, limit: int = 10):
return {
"user_id": user_id,
"limit": limit,
}
@app.get() に指定されているパスに{user_id}のように {} で書くと、その部分がパスパラメータになります。
get_user関数の引数を見ると、@app.get()のパスに存在しないパラメータ:limitがあります。これがクエリパラメータになります。
実際のリクエストがGET /users/123?limit=5の場合、FastAPIは次のように判断します。
123:パスに書かれているためuser_id → パスパラメータlimit=5:パスには書かれていないがPython関数の引数に定義されている → クエリパラメータ

クエリパラメータ名と関数の引数名は、基本的に同じ名前で対応付けられます。
DRFでは、パスパラメータやクエリパラメータは以下のように取得していた値を、FastAPIでは関数の引数として宣言すること自体がAPI仕様になるといった感じです。
self.kwargs.get("user_id")
request.query_params.get("limit")
パスパラメータを使うGET APIを実装・動作確認する

今回はまず、パスパラメータだけに集中します。クエリパラメータはまだ実装しません。
STEP 1で作成したFastAPIアプリに、次のGET APIを追加してください。
@app.get("/users/{user_id}")
def get_user(user_id: int):
return {
"user_id": user_id,
}
引数の定義でuser_id: intとintを指定しているので、FastAPIはURLから受け取った文字列を整数として扱おうとします。

APIを起動した状態で、ブラウザまたはSwagger UIから、GET /users/123へアクセスしてみてください。
期待するレスポンスは、{"user_id": 123}です。
実行結果↓

型指定によるデータ変換とバリデーションを確認する

では次にGET /users/abcへアクセスしてみてください。
実装したコードではuser_id: intと整数型を指定しています。
整数に変換できない場合にどうなるのか見てみましょう。
実行結果↓

"msg"に「有効な整数として解釈できません」というエラー内容が書かれています。

処理の流れとしてはこうです。
GET /users/abc
↓
パスの {user_id} に “abc” が入る
↓
user_id: int なので整数への変換を試みる
↓
“abc” は int に変換できない
↓
バリデーションエラー
↓
get_user() は実行されない
get_user() の中でチェックしているわけではありません。関数が呼ばれる前にFastAPI側が型を確認し、問題なければ変換済みの値を関数へ渡してくれるわけです。
クエリパラメータを使うGET APIを実装・動作確認する

今度は、クエリパラメータだけを使うAPIを追加します。
STEP 1のコードに、次を追加してください。
追加するコード↓
@app.get("/users")
def get_users(limit: int = 10):
return {
"limit": limit,
}

今回注目するのはここです。
パスには/usersしか書かれていません。一方、関数にはlimit: int = 10という引数があります。
つまり、limitは「パスには存在しないが関数の引数として定義されている」ため、FastAPIではクエリパラメータとして解釈されます。
GET /users?limit=5にアクセスしてください。期待するレスポンスは{"limit": 5}です。
実行結果↓

クエリパラメータの必須・任意・デフォルト値を理解する
省略可能なクエリパラメータ

先ほど実装したAPIの引数はlimit: int = 10となっています。
この= 10があることでlimit は省略可能なクエリパラメータになります。
つまり、GET /usersのようにlimit 自体を送らなかった場合は、limit = 10として関数が実行されます。
コードは変更せず、GET /usersにアクセスしてください。期待するレスポンスは{"limit": 10}です。
実行結果↓


コードを一時的に変更します。limit: int = 10の= 10を削除してください。
変更後のコード↓
@app.get("/users")
def get_users(limit: int):
return {
"limit": limit,
}

limitのデフォルト値をなくしたことにより、FastAPIは limit を必須のクエリパラメータとして扱います。
もう一度GET /usersへアクセスしてみてください。バリデーションエラーになるはずです。
実行結果↓


"msg"に「必須項目です」というエラー内容が書かれています。
つまり今のdef get_users(limit: int):は、limit はクエリパラメータとして必須という定義になっています。
ここまでで、まず以下の違いは確認できました。
- デフォルト値あり → 省略可能
- デフォルト値なし → 必須
任意のクエリパラメータ

次に「None を許可する任意のクエリパラメータ」 を確認します。
コードの引数の部分をlimit: int | None = Noneに変更してください。
変更後のコード↓
@app.get("/users")
def get_users(limit: int | None = None):
return {
"limit": limit,
}

int | Noneは値は int または None を許可するという意味です。= Noneは指定されなかった場合のデフォルト値は Noneという意味です。
GET /usersにアクセスしてください。期待値は{"limit": null}です。
実行結果↓


ここまでの3パターンを整理すると、つぎのようになります。
特に大事なのは、int | None だけで「省略可能」になるわけではなく、= None があるから省略可能になるという点です。
| 定義 | limit を省略できるか | 省略時 |
|---|---|---|
limit: int | ❌ | バリデーションエラー |
limit: int = 10 | ✅ | 10 |
limit: int | None = None | ✅ | None |
パスパラメータとクエリパラメータを組み合わせる

ここまで別々に扱ってきた2種類のパラメータを、今度は1つのAPIで組み合わせます。
次のGET APIを追加してください。
追加するコード↓
@app.get("/users/{user_id}/items")
def get_user_items(
user_id: int,
limit: int = 10,
):
return {
"user_id": user_id,
"limit": limit,
}

今回のコードでは、それぞれの役割がこうなります。
@app.get("/users/{user_id}/items")
# ↑ パスに存在する
# → user_id はパスパラメータ
def get_user_items(
user_id: int, # ← パスパラメータ
limit: int = 10, # ← パスには存在しない
# → クエリパラメータ
):
/users/123/items?limit=5へアクセスしてみてください。期待するレスポンスは{"user_id": 123, "limit": 5}です。
実行結果↓

Swagger UIでパラメータのAPI仕様を確認する

ここでは新しいコードは書かず、FastAPIがこれまでの定義からどのようなAPI仕様を自動生成しているかを確認します。
http://localhost:8000/docsへアクセスしてください。Swagger UIが表示されたら、今回追加した、GET /users/{user_id}/itemsを開いてください。


「Parameters」にuser_idとlimitが表示されています。ここで注目してほしいのは、FastAPIがコードのパスと引数から自動的に読み取って以下のようなAPI仕様を生成している点です。
user_id
→ path parameter
→ integer
→ 必須
limit
→ query parameter
→ integer
→ デフォルト値 10
つまり、開発者はSwagger用に別途、
「user_idはパスパラメータです」
「limitはクエリパラメータです」
とは書いていません。
Pythonの関数定義そのものがAPI仕様として利用されているわけです。
自力課題
課題内容

次の仕様のAPIを作ってください。
エンドポイント:GET /books/{book_id}/reviews
パラメータ
book_id- パスパラメータ
int- 必須
limit- クエリパラメータ
int- 省略可能
- デフォルト値は
10
offset- クエリパラメータ
intまたはNone- 省略可能
- 省略時は
None
レスポンスの例:
GET /books/25/reviews?limit=5&offset=10の場合は次のようなJSONを返してください。
{
"book_id": 25,
"limit": 5,
"offset": 10
}
また、GET /books/25/reviewsなら次のようなJSONを返してください。
{
"book_id": 25,
"limit": 10,
"offset": null
}
完了条件

次の3パターンを確認してください。
/books/25/reviews?limit=5&offset=10
/books/25/reviews
/books/abc/reviews
3つ目では、book_id が int に変換できないため、FastAPIのバリデーションエラーになることも確認してください。
実装結果
実装したコード↓
@app.get("/books/{book_id}/reviews")
def get_book_reviews(
book_id: int,
limit: int = 10,
offset: int | None = None
):
return {
"book_id": book_id,
"limit": limit,
"offset": offset,
}
エンドポイント:/books/25/reviews?limit=5&offset=10の実行結果

エンドポイント:/books/25/reviewsの実行結果

エンドポイント:/books/abc/reviewsの実行結果

ChatGPT先生のレビュー結果

ばっちりです。自力課題は要件どおりに実装できています。
確認結果も3パターンとも正しいです。
まとめ
今回はFastAPIにおけるパスパラメータとクエリパラメータの扱いについて学びました。
パスパラメータ
- 特定のリソースを指定するために用いられる
- Path Operation Decoratorのパス内に
{}で囲って記載する
クエリパラメータ
- 検索・絞り込み・並び替えなどの条件を指定する
- Path Operation Decoratorに記載はないが、関数の引数として定義されている場合、FastAPIはパスパラメータと判断
- 引数の定義にデフォルト値を指定することで省略可能にすることができる
どちらも、関数の引数定義に型を指定することで、FastAPIがAPIの仕様としてその型を採用します。
もし指定した型に変換できない値がリクエストされた場合は、関数を実行せずにバリデーションエラーを返します。
そのため、関数側でパラメータの型チェックを実装する必要がないのは手間が省けていいなと思いました。
(当たり前だが、バリデーションチェックが完全に不要になるわけではない。業務上必要なチェックは実装する必要がある。)
Path Operation Decoratorと関数の引数からAPIドキュメントにも自動でパラメータが反映されるのも便利だと感じました。
コメント