👻
FastAPIに入門する(ルーティング、バリデーション、テストとか)
前回 Hello Worldしたので、次はfast apiの基本的な機能を触ってみました。
1. 改めて、Hello Worldの実装
再掲になりますが、Hello Worldです。
1.1 基本的なAPIエンドポイント(main.py)
from fastapi import FastAPI
from app.routes.v1 import root as v1_root
app = FastAPI()
app.include_router(v1_root.router, prefix="/v1", tags=["v1"])
@app.get("/")
async def read_main():
return {"msg": "Hello World"}
1.2 テストの実装
FastAPIではTestClientを使って、簡単にAPIのテストができます。以下は、トップエンドポイントのテスト例です。
from fastapi.testclient import TestClient
from app.main import app
client = TestClient(app)
def test_read_main():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"msg": "Hello World"}
2. ディレクトリ構成とファイルの中身
ディレクトリ構成です。テスト含め3ファイルのみです。
app/
routes/
v1/
root.py # APIルーターが定義されているファイル
main.py # アプリケーションのエントリーポイント
tests/
test_main.py # テストコード
2.1 main.py
ルーティングをまとめるために、APIエンドポイントをroot.pyに記述し、app.include_routerで読み込みます。
# main.py
from fastapi import FastAPI
from app.routes.v1 import root as v1_root
app = FastAPI()
app.include_router(v1_root.router, prefix="/v1", tags=["v1"])
@app.get("/")
async def read_main():
return {"msg": "Hello World"}
2.2 root.py
具体的なルーティング、リクエストボディのバリデーション、そしてレスポンスの定義を行います。ファイルは分けたほうがよいですが、機能の確認なのでヨシ。
# root.py
from fastapi import APIRouter
from pydantic import BaseModel, Field
from enum import Enum
from starlette.responses import JSONResponse
from starlette.status import HTTP_200_OK
router = APIRouter()
class CategoryEnum(str, Enum):
tech = "tech"
life = "life"
other = "other"
class ExampleRequest(BaseModel):
example_name: str = Field(..., min_length=3, max_length=50, description="3文字以上50文字以下の名前")
description: str = Field(..., max_length=200, description="最大200文字までの説明")
category: CategoryEnum = Field(..., description="カテゴリ(必須)")
class ExampleResponse(BaseModel):
example_name: str
description: str
category: CategoryEnum
message: str
@router.post(
"/examples/",
status_code=HTTP_200_OK,
response_model=ExampleResponse,
summary="サンプルデータの作成",
description="リクエストされたデータをバリデーションし、レスポンスとして返します。"
)
async def create_example(data: ExampleRequest) -> JSONResponse:
return JSONResponse(
{
"example_name": data.example_name,
"description": data.description,
"category": data.category,
"message": "Example received and validated successfully"
}
)
3. ルーティング
FastAPIでは、APIRouterを使ってエンドポイントをグループ化することができます。
例えば、上記のroot.pyでは、/v1というプレフィックスを付けることで、バージョン管理が容易になります。
# main.py
from fastapi import FastAPI
from app.routes.v1 import root as v1_root
app = FastAPI()
app.include_router(v1_root.router, prefix="/v1", tags=["v1"])
4. バリデーション
FastAPIは、Pydanticを利用してリクエストボディのデータバリデーションを自動で行います。
4.1 Pydanticによるバリデーションの仕組み
ExampleRequestクラスでは、以下のようなバリデーションを定義しています。
-
example_name
- 最小3文字、最大50文字の文字列
- descriptionを書くと、生成されるドキュメントに書いてくれます。動作には影響ないです。
-
description
- 最大200文字までの文字列
-
category
- 定義されたEnumの値(“tech”、“life”、“other”)のいずれかである必要がある
リクエストがこれらの制約に違反した場合、FastAPIは自動的にエラーレスポンス(HTTP 422 Unprocessable Entity)を返してくれます。
- 定義されたEnumの値(“tech”、“life”、“other”)のいずれかである必要がある
4.2 実際のリクエストとレスポンスの例
例えば、以下のような正しいJSONデータを送信すると、期待したレスポンスが返ります。
リクエスト例:
{
"example_name": "FastAPI入門",
"description": "FastAPIの基本を学ぶサンプル",
"category": "tech"
}
レスポンス例:
{
"example_name": "FastAPI入門",
"description": "FastAPIの基本を学ぶサンプル",
"category": "tech",
"message": "Example received and validated successfully"
}
エラー例(HTTP 422):
{
"detail": [
{
"loc": [
"body",
"example_name"
],
"msg": "ensure this value has at least 3 characters",
"type": "value_error.any_str.min_length",
"ctx": {
"limit_value": 3
}
}
]
}
6. まとめ
fastapiは…
- ルーティング:グルーピングして分離できる。
- バリデーション:デフォルトで入ってる。
- テスト:pytestを入れればできる
ORMは無いみたい。あと、依存注入は微妙だった。たぶん関数型のパラダイムをよく分かっていないからかもしれない。
Discussion