セットアップ
最小アプリ
from fastapi import FastAPI app = FastAPI() @app.get("/") async def root(): return {"message": "Hello, World!"}
アプリの起動
pip install "fastapi[standard]" fastapi dev main.py # 自動リロード付き開発モード fastapi run main.py # 本番環境
主な特徴
非同期ネイティブasync/await と ASGI(Uvicorn)
自動ドキュメントSwagger UI は /docs、ReDoc は /redoc
型バリデーションリクエスト/レスポンスに Pydantic モデル
OpenAPIOpenAPI スキーマの自動生成
依存性注入組み込みの DI システム
パスオペレーション
HTTP メソッド
@app.get("/items") @app.post("/items") @app.put("/items/{item_id}") @app.patch("/items/{item_id}") @app.delete("/items/{item_id}")
パスパラメーター
@app.get("/users/{user_id}") async def get_user(user_id: int): return {"user_id": user_id} # Enum 制約 from enum import Enum class Color(str, Enum): red = "red" blue = "blue"
ステータスコードとタグ
from fastapi import status @app.post("/items", status_code=status.HTTP_201_CREATED, tags=["items"]) async def create_item(item: Item): return item
リクエストボディ
Pydantic モデル
from pydantic import BaseModel, Field class Item(BaseModel): name: str price: float = Field(gt=0, description="Must be positive") tags: list[str] = []
ネストモデル
class Address(BaseModel): street: str city: str zip_code: str class User(BaseModel): name: str address: Address
エンドポイントでの使用
@app.post("/items") async def create_item(item: Item): return {"name": item.name, "price": item.price}
バリデーション機能
Field(gt=0)0 より大きい
Field(min_length=1)最小文字列長
Field(max_length=100)最大文字列長
Field(pattern='^[a-z]+$')正規表現パターン一致
Field(default=None)デフォルト値付きのオプション
EmailStrメールバリデーション(pydantic[email])
クエリパラメーター
基本クエリパラメーター
@app.get("/items") async def list_items(skip: int = 0, limit: int = 10): return items[skip : skip + limit] # GET /items?skip=0&limit=20
クエリバリデーション
from fastapi import Query @app.get("/search") async def search( q: str = Query(min_length=3, max_length=50), page: int = Query(default=1, ge=1), ): return {"q": q, "page": page}
オプションと必須
async def read_items( q: str | None = None, # オプション name: str = ..., # 必須(省略記号) tags: list[str] = Query(default=[]), ): return {"q": q, "name": name}
ヘッダーと Cookie
from fastapi import Header, Cookie async def read( user_agent: str | None = Header(default=None), session_id: str | None = Cookie(default=None), ): return {"ua": user_agent}
レスポンスモデル
レスポンスモデル
class ItemOut(BaseModel): name: str price: float @app.get("/items/{id}", response_model=ItemOut) async def get_item(id: int): return items[id] # 余分なフィールドをフィルタリング
複数のレスポンス型
from fastapi.responses import JSONResponse, HTMLResponse @app.get("/html", response_class=HTMLResponse) async def get_html(): return "

Hello

"
レスポンスモデルオプション
response_model出力フィルタリング用 Pydantic モデル
response_model_exclude_unset明示的に設定されていないフィールドを省略
response_model_include特定フィールドのホワイトリスト
response_model_exclude特定フィールドのブラックリスト
エラーレスポンス
from fastapi import HTTPException @app.get("/items/{id}") async def get_item(id: int): if id not in items: raise HTTPException(status_code=404, detail="Not found") return items[id]
依存性
関数依存性
from fastapi import Depends async def get_db(): db = SessionLocal() try: yield db finally: db.close()
エンドポイントでの使用
@app.get("/users") async def list_users(db: Session = Depends(get_db)): return db.query(User).all()
クラスベース依存性
class Pagination: def __init__(self, skip: int = 0, limit: int = 10): self.skip = skip self.limit = limit @app.get("/items") async def list_items(pg: Pagination = Depends()): return items[pg.skip : pg.skip + pg.limit]
依存性のスコープ
Depends(func)エンドポイントごとの依存性
app = FastAPI(dependencies=[...])全ルートへのグローバル依存性
APIRouter(dependencies=[...])ルーターレベルの依存性
yieldセットアップ/ティアダウン(DB セッション、ロックなど)
認証
OAuth2 パスワードベアラー
from fastapi.security import OAuth2PasswordBearer oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") @app.get("/users/me") async def read_me(token: str = Depends(oauth2_scheme)): user = decode_token(token) return user
JWT トークンフロー
from jose import jwt SECRET = "your-secret-key" def create_token(data: dict): return jwt.encode(data, SECRET, algorithm="HS256") def decode_token(token: str): return jwt.decode(token, SECRET, algorithms=["HS256"])
トークンエンドポイント
from fastapi.security import OAuth2PasswordRequestForm @app.post("/token") async def login(form: OAuth2PasswordRequestForm = Depends()): user = authenticate(form.username, form.password) if not user: raise HTTPException(status_code=401) return {"access_token": create_token({"sub": user.id})}
セキュリティスキーム
OAuth2PasswordBearerフォームログイン経由のベアラートークン
HTTPBasicBasic ユーザー名/パスワード認証
APIKeyHeaderヘッダーの API キー
APIKeyCookieCookie の API キー
バックグラウンドタスク
シンプルなバックグラウンドタスク
from fastapi import BackgroundTasks def send_email(to: str, body: str): # レスポンス後に実行される低速な処理 email_client.send(to, body) @app.post("/notify") async def notify(bg: BackgroundTasks): bg.add_task(send_email, "[email protected]", "Hello!") return {"status": "queued"}
バックグラウンド付き依存性
async def log_request(bg: BackgroundTasks): bg.add_task(write_log, "request received") @app.get("/items", dependencies=[Depends(log_request)]) async def list_items(): return items
バックグラウンド vs ワーカー
BackgroundTasksレスポンス後の軽量タスク(メール、ログ)
Celery / ARQ別ワーカーが必要な重い処理
asyncio.create_taskファイアアンドフォーゲットの非同期コルーチン
ミドルウェア
カスタムミドルウェア
import time from starlette.middleware.base import BaseHTTPMiddleware class TimingMiddleware(BaseHTTPMiddleware): async def dispatch(self, request, call_next): start = time.time() response = await call_next(request) duration = time.time() - start response.headers["X-Process-Time"] = str(duration) return response
ミドルウェアの追加
app.add_middleware(TimingMiddleware)
CORS
from fastapi.middleware.cors import CORSMiddleware app.add_middleware( CORSMiddleware, allow_origins=["https://example.com"], allow_methods=["*"], allow_headers=["*"], )
組み込みミドルウェア
CORSMiddlewareクロスオリジンリソース共有
TrustedHostMiddleware許可されたホスト名を制限
GZipMiddlewareGzip レスポンス圧縮
HTTPSRedirectMiddlewareHTTP を HTTPS にリダイレクト
テスト
テストクライアント
from fastapi.testclient import TestClient client = TestClient(app) def test_read_root(): resp = client.get("/") assert resp.status_code == 200 assert resp.json() == {"message": "Hello, World!"}
POST のテスト
def test_create_item(): resp = client.post("/items", json={ "name": "Widget", "price": 9.99, }) assert resp.status_code == 201 assert resp.json()["name"] == "Widget"
依存性のオーバーライド
async def mock_db(): return FakeDB() app.dependency_overrides[get_db] = mock_db def test_with_mock_db(): resp = client.get("/users") assert resp.status_code == 200
非同期テスト
import pytest from httpx import AsyncClient, ASGITransport @pytest.mark.anyio async def test_async(): transport = ASGITransport(app=app) async with AsyncClient(transport=transport) as ac: resp = await ac.get("/") assert resp.status_code == 200