- FastAPI のルーティングの方法が知りたい
- バリデーションってどうやるの?
このような疑問にお答えします。
FastAPI では関数を使ってルーティングを表現するので、端的で読みやすいです。
本記事ではルーティングにまつわる以下のような内容を解説します。
- 各パラメータの見分け方
- エンドポイントの作成方法
- パスパラメータを扱う方法
- リクエストボディを扱う方法
- エンドポイントの柔軟性を高める設計のヒント
FastAPIを初めて触る方でも理解しやすいよう、できるだけ平易に解説していきます。
FastAPI での各パラメータの見分け方
パラメータには以下の種類があります。
- パスパラメータ
- クエリパラメータ
- リクエストボディ
FastAPI のコードは一見するとこれらの違いが分かりにくいので、ここで見分け方を解説します。
パスパラメータ
パスパラメータは、URLのパスの一部として表現されます。
つまりURL 定義をしている部分のうち、波括弧({}
)の中身がパスパラメータです。
@app.get("/users/{user_id}")
async def get_user(user_id: int):
return {"user_id": user_id}
上記の例では、user_id
がパスパラメータになります。
このパスパラメータを関数内で扱うため、「デコレータの波括弧内で宣言した変数名」と同じものを関数の引数として指定しています。
クエリパラメータ
クエリパラメータは URL の?
の後に指定されます。
Key と Value の組み合わせで表現されるタイプのパラメータです。
FastAPI では、関数の引数がデフォルト値を持つ場合にクエリパラメータと解釈します。
@app.get("/items/")
async def read_items(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}
skip
とlimit
はデフォルト値が設定されているので、どちらもクエリパラメータです。
これはフィルタや設定オプションに使われることが多いです。
リクエストボディ
リクエストボディは「リクエストの本体部分に含まれるデータ」で、JSON などの形式で送信されることが多いです。
FastAPI では、Pydantic の BaseModel を継承したクラスを引数とした場合にリクエストボディと判定されます。
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str
@app.post("/items/")
async def create_item(item: Item):
return item
上記の例を見てみると、item
という引数は BaseModel を継承した Item クラスを型に指定しています。
そのため、item
はリクエストボディから取得されます。
FastAPI におけるエンドポイントの作成方法
基本的なルート設定方法
FastAPI では「各HTTPメソッドごとに用意されているデコレータ」を使います。
非同期通信を行うため、async def
を使っていきます。
GET リクエスト
GET メソッドはリソースを読み出すためのメソッドです。
from fastapi import FastAPI
app = FastAPI()
@app.get("/items")
async def read_items():
return {"message": "GETリクエストを受け取りました"}
リソースに変更を加える場合には、POST メソッド以降を利用します。
POST リクエスト
新しいリソースを作成する場合に使われるのが POST メソッドです。
以下のように記述することで、POST リクエストに含まれるリクエストボディitem
を受け取ることができます。
from fastapi import FastAPI
app = FastAPI()
@app.post("/items")
async def create_item(item: dict):
return {"message": "アイテムを作成しました", "item": item}
受け取ったJSON に対して、便宜的にitem
という名前をつけたと考えると理解しやすいです。
つまり、クライアント側からは POST メソッドで以下のような JSON をリクエストボディに含めることになります。
{
"name": "example item",
"price": 1500
}
FastAPI 側ではこの JSON を Python の辞書オブジェクトとして解釈することになります。
PUT リクエスト
PUT はリソースを作成または置換するために使用されます。
リソースがすでにある場合は更新し、ない場合は新規作成します。
from fastapi import FastAPI
app = FastAPI()
@app.put("/items/{item_id}")
async def update_item(item_id: int, item: dict):
return {"message": "アイテムを更新しました", "item_id": item_id, "item": item}
上記では、「リクエストボディ」と「パスパラメータ」からデータを受け取るようになっています。
DELETE リクエスト
DELETE はリソースを削除するメソッドです。
from fastapi import FastAPI
app = FastAPI()
@app.delete("/items/{item_id}")
async def delete_item(item_id: int):
return {"message": "アイテムを削除しました", "item_id": item_id}
特定のitem_id
に対してリソースの削除をするコード例になります。
FastAPI でパスパラメータを扱う方法
パスパラメータは「URL の一部に動的な値」を指定する方法になります。
パスパラメータは渡されないと「不完全なURL」とみなされる点には注意が必要です。
パスパラメータの基本的な使い方
FastAPI では、「デコレータ内」と「関数の引数」でパスパラメータを定義します。
以下はパスパラメータとしてitem_id
を定義した例です。
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: int):
return {"item_id": item_id, "description": "こちらがアイテムの説明です。"}
関数の引数に型注釈としてint
が指定されているので、FastAPI は以下のことを行います。
- item_idは整数型に変換される
- 整数型にできなければエラーを返す
例えば型注釈としてint
を指定したにも関わらず “abc” を渡した場合には、”422 Unprocessable Entity” といステータスコードを返却します。
使えるデータは幅広く、例えば次のようなものがあります。
- Python 標準のデータ型(str, float, list, set など)
- Pydantic のデータ型(BaseModel, EmailStr など)
- オプショナル型、リテラル型、ユニオン型
2つ以上のパスパラメータを扱う方法
複数のパスパラメータを扱う場合も、考え方は同じです。
パスパラメータとしてuser_id
とitem_id
の二つを持つ場合の例を示します。
@app.get("/users/{user_id}/items/{item_id}")
async def read_user_item(user_id: int, item_id: int):
return {"user_id": user_id, "item_id": item_id, "description": "ユーザーとアイテムの情報"}
パスパラメータにスラッシュを含む場合の対策
/folder1/file1.txt
というまとまりをパスパラメータとして取得したい場合を考えてみましょう。
普通にコードを書いてしまうと「スラッシュ」が境界線と判断されるので、folder1
とfile1.txt
が別の要素として取得されてしまいます。
これを一つのパスパラメータとして扱うには、”path” タイプを指定します。
@app.get("/files/{file_path:path}")
async def read_file(file_path: str):
return {"file_path": file_path}
こうすることでfile_path
に対して/folder1/file1.txt
が渡されても、スラッシュで分割されずに要素を取得できることになります。
FastAPI でクエリパラメータを扱う方法
FastAPIでは、関数の引数にデフォルト値を設定することで、クエリパラメータになります。
つまり、クエリパラメータが省略された場合にはデフォルト値が使用されます。
基本的なクエリパラメータの受け取り方
skip
とlimit
には初期値を設定したので、それぞれ二つの要素ともクエリパラメータとして扱われます。
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}
クライアント側から見ると、以下のようなリクエストが送信されることを想定。
/items/?skip=20&limit=10
上記の場合、FastAPI は “skip” には “20” を、”limit” には “10” を受け取ることになります。
クエリパラメータをオプショナルにする
オプショナルにするには、初期値としてNone
を指定します。
@app.get("/items/")
async def read_items(skip: int = 0, limit: int = 10, tag: Optional[str] = None):
items = {"skip": skip, "limit": limit}
if tag:
items.update({"tag": tag})
return items
None
を書かない場合、必須のクエリパラメータとなります。
クエリパラメータのバリデーション
型宣言を使用してバリデーションが可能です。
@app.get("/items/")
async def read_items(skip: int = 0, limit: int = 10):
return {"skip": skip, "limit": limit}
この例では、以下のバリデーションを追加しました。
skip
: 整数、デフォルト値は0limit
: 整数、デフォルト値は10
デフォルト値とは、クエリパラメータが渡されない場合の初期値です。
Pydantic を使ったより複雑なバリデーション
Pydanticの Field
を使うことで、より複雑なバリデーションを行えます。
以下の例は、クエリパラメータで受け取る数値に「最小値」と「最大値」を設定しています
from pydantic import BaseModel, Field
from typing import Optional
class QueryParams(BaseModel):
skip: int = Field(default=0, ge=0)
limit: int = Field(default=10, ge=1, le=100)
@app.get("/items/")
async def read_items(query: QueryParams = Depends()):
return {"skip": query.skip, "limit": query.limit}
このコードで行なっていることは以下です。
skip
: 0以上limit
: 1以上100以下
ちなみに、数値の比較に使われる名前は以下の意味があるそうです。
ge
(>=): greater than or equalgt
(>): greater thanle
(<=): less than or equallt
(<): less than
どれがどの効果を持つか思い出す際に便利です。
FastAPI でリクエストボディを扱う方法
FastAPI では、リクエストボディに対して複雑なバリデーションを行うことができます。
Pydanticモデルを定義して「型宣言」と「バリデーションルール」を組み込むことで対応することになります。
以下の例ではItem
というリクエストボディのバリデーションを行っています。
from fastapi import FastAPI
from pydantic import BaseModel, Field
class Item(BaseModel):
name: str
description: str = None
price: float = Field(gt=0, description="The price must be greater than zero")
tax: float = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
return {"name": item.name, "price": item.price}
Item
クラス内の要素の定義は以下です。
name
: 必須項目description
: オプショナルprice
: 0より大きい値tax
: オプショナル
初期値ありは必須項目、初期値なしはオプショナル(任意)の項目ということになります。
上記のようにField
関数を使用すると、詳細なバリデーションルールやメタデータをフィールドに追加できます。
エンドポイントの柔軟性を高める設計のヒント
API をより使いやすくするためのヒントをご紹介します。
リソース指向のアプローチ
各HTTPメソッドの意味に沿った処理内容にしましょう。
HTTPメソッドと処理内容の関係は以下の通りです。
HTTPメソッド | 処理内容 |
---|---|
GET | リソースを読み出すが変更はしない |
POST | 新規リソースを作成 |
PUT | リソースの更新 or 置換 |
DELETE | リソースの削除 |
PATCH | リソースの部分的な更新 |
これらを遵守することで「直感的で分かりやすいエンドポイント」が作成できます。
パスパラメータとクエリパラメータを使い分ける
「パスパラメータ」と「クエリパラメータ」はそれぞれ用途や特性が異なります。
基本的には以下の使い分けがされることが多いです。
- パスパラメータ: リソースの「特定」
- クエリパラメータ:リソースの「操作」
リソースの特定とは、例えば「ユーザーID」や「製品ID」などを指定して、個別具体的なアイテムを取得してくるイメージです。
一方のリソースの「操作」とは、「ページネーション」「フィルタリング」「ソート」などのリソースに対する加工のための追加情報を渡す形になります。
バリデーションとエラー処理の強化
FastAPI では、Pydantic モデルを使った強力なバリデーションが可能です。
不正 / 不完全なリクエストに適切なエラーを返すことで、システム全体としてのセキュリティを上げていきましょう。
バージョニング
将来的な API の変更に備え、最初からバージョニングを組み込みましょう。
URL にバージョン番号を含めることで API の新旧バージョンを並行して運用することや、徐々に移行することもできるようになります。
セキュリティと認証
OAuth2 や JWT を活用し、安全なAPIエンドポイントを作成しましょう。
API ルーティングの知識を設計に生かそう!
FastAPI を使った API ルーティングの基礎をお伝えしてきました。
以上の内容を理解して上手に API 設計を行なっていきましょう。
コメント