取り組みの背景 — Claude Code × FastAPI で何が変わるか
Python バックエンド開発において、FastAPI は型安全・高速・非同期処理の三拍子が揃った事実上の標準フレームワークとなりましました。しかし、設計からテスト・デプロイまでを一人の開発者が高品質に仕上げるには、膨大な知識と時間が必要です。
Claude Code を組み合わせることで、その状況は大きく変わります。ここで扱うのはClaude Code を真の「AIペアプログラマー」として活用し、FastAPI 本番 API サーバーを設計からデプロイまで完全に構築する方法を実装パターン中心にまとめます。
この記事で学べること
この記事は次のような方を対象に書かれています。
- Python と FastAPI の基本は知っているが、本番品質の設計・テストに自信がない方
- Claude Code を使い始めたが、バックエンド開発への活用方法がわからない方
- pytest・Docker・CI/CD を Claude Code と組み合わせて効率化したい方
- 個人開発 SaaS や副業 API サーバーを短期間で構築したい方
この記事を読み終えると、Claude Code を使って FastAPI プロジェクトをゼロから本番環境にデプロイする完全なワークフローを習得できます。
前提知識・準備
必要な環境
本記事のコードを実行するには、以下の環境が必要です。
- Python 3.12 以上
- Claude Code CLI(最新版)
- Docker Desktop
- Git
Claude Code のインストールと初期設定については、Claude Code プロジェクト初期化完全自動化ガイドをご参照ください。
プロジェクト構成の全体像
今回構築する API サーバーの構成は以下の通りです。
fastapi-app/
├── CLAUDE.md # Claude Code 向け設計指針
├── app/
│ ├── main.py # FastAPI アプリ本体
│ ├── api/
│ │ ├── v1/
│ │ │ ├── router.py # v1 ルーター
│ │ │ ├── users.py # ユーザーエンドポイント
│ │ │ └── items.py # アイテムエンドポイント
│ ├── core/
│ │ ├── config.py # 環境変数・設定管理
│ │ ├── security.py # 認証・JWT
│ │ └── database.py # SQLAlchemy セッション管理
│ ├── models/
│ │ ├── user.py # SQLAlchemy モデル
│ │ └── item.py
│ └── schemas/
│ ├── user.py # Pydantic v2 スキーマ
│ └── item.py
├── tests/
│ ├── conftest.py # pytest フィクスチャ
│ ├── unit/
│ └── integration/
├── Dockerfile
├── docker-compose.yml
└── .github/workflows/ci.yml
CLAUDE.md の設計 — AIに正確なコンテキストを与える
Claude Code がプロジェクトを正確に理解するには、CLAUDE.md の設計が最重要です。これは Claude Code に対する「技術仕様書」であり、適切に書かれた CLAUDE.md があるかどうかで、生成されるコードの品質が劇的に変わります。
CLAUDE.md の記述内容
以下の CLAUDE.md を fastapi-app/ のルートに配置します。
# FastAPI Production API — CLAUDE.md
## 技術スタック
- Python 3.12 / FastAPI 0.115+ / Uvicorn
- SQLAlchemy 2.0 (async) + asyncpg (PostgreSQL)
- Pydantic v2(フィールドバリデーション厳格モード)
- JWT 認証(python-jose + passlib bcrypt)
- pytest-asyncio + httpx(非同期テスト)
- Docker + docker-compose
## コーディング規約
- すべての関数・クラスに型ヒントを付ける
- async/await を一貫して使用(同期コードを混在させない)
- エンドポイントのレスポンスモデルは必ず Pydantic スキーマで定義
- DBセッションは依存性注入(Depends)で取得する
- HTTPException は app/core/exceptions.py に集約する
## テスト規約
- 新機能実装時は必ず pytest テストを同時作成する
- ユニットテスト: app/core/ の純粋関数
- 統合テスト: 実際の DB(テスト用 SQLite in-memory)を使用
- カバレッジ目標: 80% 以上
## 禁止事項
- グローバル変数への状態保存
- print デバッグ(logging モジュールを使う)
- ハードコードされた認証情報(必ず環境変数)
この CLAUDE.md があることで、Claude Code は「このプロジェクトでは非同期 SQLAlchemy を使う」「テストは pytest-asyncio で書く」といったコンテキストを自動的に把握し、一貫性のあるコードを生成します。
Step 1: プロジェクト初期化 — Claude Code で骨格を生成する
まず Claude Code を使ってプロジェクト全体の骨格を生成します。
# プロジェクトディレクトリ作成
mkdir fastapi-app && cd fastapi-app
# CLAUDE.md を先に配置(上記内容で)
# Claude Code を起動
claude
Claude Code が起動したら、以下のプロンプトを入力します。
CLAUDE.md の仕様に従って、FastAPI 本番 API の完全なプロジェクト骨格を生成してください。
要件:
1. app/core/config.py — pydantic-settings を使った環境変数管理
2. app/core/database.py — SQLAlchemy 2.0 async エンジン + セッションファクトリ
3. app/core/security.py — JWT アクセストークン生成・検証
4. app/main.py — lifespan イベント・CORS・ミドルウェア設定済み
5. requirements.txt — バージョン固定済み
6. pyproject.toml — ruff + mypy 設定
各ファイルを実際に作成してください。
生成されたコードの例
Claude Code が生成する app/core/config.py は以下のようになります。
# app/core/config.py
from pydantic_settings import BaseSettings, SettingsConfigDict
from functools import lru_cache
class Settings(BaseSettings):
"""アプリケーション設定。環境変数から自動読み込み。"""
model_config = SettingsConfigDict(
env_file=".env",
env_file_encoding="utf-8",
case_sensitive=False,
)
# アプリ設定
app_name: str = "FastAPI Production API"
app_version: str = "1.0.0"
debug: bool = False
# DB 設定
database_url: str = "postgresql+asyncpg://user:password@localhost/dbname"
database_pool_size: int = 10
database_max_overflow: int = 20
# JWT 設定
secret_key: str # 必須: 本番では 256bit ランダム文字列
algorithm: str = "HS256"
access_token_expire_minutes: int = 30
# CORS 設定
allowed_origins: list[str] = ["http://localhost:3000"]
@lru_cache
def get_settings() -> Settings:
"""シングルトンで設定を返す(DI用)。"""
return Settings()
@lru_cache でシングルトン化することで、Depends(get_settings) で注入したときに毎回ファイルを読み直すコストを避けられます。Claude Code はこういった本番品質のベストプラクティスを自動で組み込んでくれます。
Step 2: モデルとスキーマ設計 — Pydantic v2 × SQLAlchemy 2.0
Claude Code を使ってモデル設計を行う際のポイントは、「要件を自然言語で詳細に伝える」ことです。
app/models/user.py と app/schemas/user.py を作成してください。
ユーザーモデルの要件:
- id: UUID(自動生成)
- email: 一意制約、255文字以内
- hashed_password: str
- is_active: bool(デフォルト True)
- is_superuser: bool(デフォルト False)
- created_at / updated_at: タイムスタンプ自動管理
Pydantic スキーマは以下の3種類:
- UserCreate: パスワード入力用(プレーンテキスト)
- UserRead: APIレスポンス用(パスワード非公開)
- UserUpdate: 部分更新用(全フィールドOptional)
SQLAlchemy 2.0 の Mapped[T] 型アノテーション方式で書くこと。
生成された app/models/user.py の核心部分:
# app/models/user.py
import uuid
from datetime import datetime
from sqlalchemy import String, Boolean, func
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
class Base(DeclarativeBase):
pass
class User(Base):
__tablename__ = "users"
id: Mapped[uuid.UUID] = mapped_column(
primary_key=True, default=uuid.uuid4, index=True
)
email: Mapped[str] = mapped_column(
String(255), unique=True, index=True, nullable=False
)
hashed_password: Mapped[str] = mapped_column(nullable=False)
is_active: Mapped[bool] = mapped_column(Boolean, default=True)
is_superuser: Mapped[bool] = mapped_column(Boolean, default=False)
created_at: Mapped[datetime] = mapped_column(
default=func.now(), server_default=func.now()
)
updated_at: Mapped[datetime] = mapped_column(
default=func.now(), onupdate=func.now(), server_default=func.now()
)
SQLAlchemy 2.0 の Mapped[T] アノテーションは、IDE の型補完とも完全に連携し、mypy によるスタティック解析も通ります。Claude Code はこの最新スタイルを正確に把握して生成します。
Step 3: APIエンドポイント開発 — ルーティング設計とビジネスロジック
エンドポイントの実装は、CRUD の単純な実装から始めて、段階的に複雑な要件を追加するのがコツです。
ユーザー CRUD エンドポイントの生成
app/api/v1/users.py を実装してください。
エンドポイント一覧:
- POST /users/ — 新規ユーザー登録(重複メールチェック付き)
- GET /users/me — 現在のログインユーザー取得
- GET /users/{user_id} — ユーザー詳細取得(スーパーユーザーのみ)
- PATCH /users/{user_id} — ユーザー情報更新
- DELETE /users/{user_id} — ユーザー削除(論理削除: is_active=False)
共通要件:
- すべて async def で実装
- DB操作は SQLAlchemy async Session を Depends で取得
- 認証済みユーザーの確認は get_current_user 依存性を使用
- 適切な HTTPException(400/401/403/404)を返す
- レスポンスは UserRead スキーマで型付け
生成されたエンドポイントの例:
# app/api/v1/users.py
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select
from app.core.database import get_db
from app.core.security import get_current_user, get_password_hash
from app.models.user import User
from app.schemas.user import UserCreate, UserRead, UserUpdate
router = APIRouter(prefix="/users", tags=["users"])
@router.post("/", response_model=UserRead, status_code=status.HTTP_201_CREATED)
async def create_user(
user_in: UserCreate,
db: AsyncSession = Depends(get_db),
) -> UserRead:
"""新規ユーザーを登録する。メールアドレス重複時は 400 を返す。"""
# 重複チェック
result = await db.execute(select(User).where(User.email == user_in.email))
existing = result.scalar_one_or_none()
if existing:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="このメールアドレスは既に登録されています。",
)
# パスワードハッシュ化
hashed_pw = get_password_hash(user_in.password)
db_user = User(email=user_in.email, hashed_password=hashed_pw)
db.add(db_user)
await db.commit()
await db.refresh(db_user)
return db_user
@router.get("/me", response_model=UserRead)
async def read_current_user(
current_user: User = Depends(get_current_user),
) -> UserRead:
"""現在の認証済みユーザー情報を返す。"""
return current_user
このコードは response_model=UserRead を指定することで、hashed_password が自動的にレスポンスから除外されます。Claude Code に型安全な設計を徹底させるには、CLAUDE.md でその旨を明記する点が肝心です。
Step 4: pytest 自動テスト — 非同期APIの完全テスト実装
FastAPI + SQLAlchemy async の組み合わせのテストは、設定が煩雑になりがちです。Claude Code に任せることで、この複雑な conftest.py をすぐに生成できます。
conftest.py の生成プロンプト
tests/conftest.py を作成してください。
要件:
- テスト用 SQLite in-memory DB(aiosqlite)を使用
- 各テストの前後でDBを初期化(isolation 保証)
- pytest-asyncio の asyncio_mode = "auto"
- httpx.AsyncClient でエンドポイントをテスト
- テスト用ユーザー生成フィクスチャ(通常ユーザー + スーパーユーザー)
- JWT トークン生成フィクスチャ
pytest.ini も合わせて作成すること。
生成された tests/conftest.py:
# tests/conftest.py
import pytest
import pytest_asyncio
from httpx import AsyncClient, ASGITransport
from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine
from sqlalchemy.pool import StaticPool
from app.main import app
from app.core.database import Base, get_db
from app.core.security import get_password_hash, create_access_token
from app.models.user import User
# テスト用インメモリ SQLite エンジン
TEST_DATABASE_URL = "sqlite+aiosqlite:///:memory:"
test_engine = create_async_engine(
TEST_DATABASE_URL,
connect_args={"check_same_thread": False},
poolclass=StaticPool, # in-memory 共有のため必須
)
@pytest_asyncio.fixture(autouse=True)
async def setup_db():
"""各テスト前にテーブルを作成し、テスト後に削除する。"""
async with test_engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
yield
async with test_engine.begin() as conn:
await conn.run_sync(Base.metadata.drop_all)
@pytest_asyncio.fixture
async def db_session():
"""テスト用 DB セッションを提供する。"""
async with AsyncSession(test_engine) as session:
yield session
@pytest_asyncio.fixture
async def client(db_session: AsyncSession):
"""DB を差し替えた FastAPI テストクライアントを提供する。"""
app.dependency_overrides[get_db] = lambda: db_session
async with AsyncClient(
transport=ASGITransport(app=app), base_url="http://test"
) as ac:
yield ac
app.dependency_overrides.clear()
@pytest_asyncio.fixture
async def normal_user(db_session: AsyncSession) -> User:
"""テスト用通常ユーザーを作成する。"""
user = User(
email="test@example.com",
hashed_password=get_password_hash("testpassword123"),
)
db_session.add(user)
await db_session.commit()
await db_session.refresh(user)
return user
@pytest_asyncio.fixture
def user_token(normal_user: User) -> str:
"""テスト用アクセストークンを生成する。"""
return create_access_token(subject=str(normal_user.id))
統合テストの例
# tests/integration/test_users.py
import pytest
from httpx import AsyncClient
@pytest.mark.asyncio
async def test_create_user_success(client: AsyncClient):
"""正常なユーザー登録のテスト。"""
response = await client.post(
"/api/v1/users/",
json={"email": "new@example.com", "password": "securepass123"},
)
assert response.status_code == 201
data = response.json()
assert data["email"] == "new@example.com"
assert "hashed_password" not in data # パスワードが非公開であることを確認
@pytest.mark.asyncio
async def test_create_user_duplicate_email(client: AsyncClient, normal_user):
"""重複メールアドレスで 400 が返ることを確認。"""
response = await client.post(
"/api/v1/users/",
json={"email": normal_user.email, "password": "anypassword"},
)
assert response.status_code == 400
assert "既に登録" in response.json()["detail"]
@pytest.mark.asyncio
async def test_read_current_user(client: AsyncClient, user_token: str):
"""認証済みユーザーが /me エンドポイントにアクセスできることを確認。"""
response = await client.get(
"/api/v1/users/me",
headers={"Authorization": f"Bearer {user_token}"},
)
assert response.status_code == 200
assert response.json()["email"] == "test@example.com"
このテスト設計のポイントは、本番DBに依存せず、完全にインメモリで動作することです。app.dependency_overrides で本番の get_db をテスト用セッションに差し替える手法は、FastAPI の依存性注入システムを最大限に活用しています。
カバレッジレポートを確認するには:
# カバレッジ付きテスト実行
pytest --cov=app --cov-report=html tests/
# htmlcov/index.html でカバレッジを視覚的に確認
# 期待する出力例:
# ==================== 12 passed in 1.83s ====================
# TOTAL: 84%
Step 5: Claude Code Hooks でテストを自動化する
Claude Code Hooks 自動化テクニック集で詳しく解説されていますが、Hooks を使うことでファイル保存時に自動でテストを実行できます。
.claude/settings.json の設定
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "cd /path/to/fastapi-app && python -m pytest tests/ -x -q --tb=short 2>&1 | tail -20"
}
]
}
]
}
}
この設定により、Claude Code がファイルを編集するたびにテストが自動実行されます。テストが失敗すると Claude Code は失敗を認識し、自動的に修正を試みます。これにより、「コード生成 → テスト失敗 → 自動修正」のサイクルが確立され、開発速度と品質が同時に向上します。
Step 6: Docker化 — 本番グレードのコンテナ設計
Dockerfile の生成
本番グレードの Dockerfile を作成してください。
要件:
- マルチステージビルド(builder + runtime)
- Python 3.12-slim ベースイメージ
- 非root ユーザーで実行(セキュリティ)
- .dockerignore で不要ファイルを除外
- イメージサイズを最小化(200MB以下を目標)
- ヘルスチェック設定
生成された Dockerfile:
# ── ステージ1: 依存関係ビルド ──
FROM python:3.12-slim AS builder
WORKDIR /build
# ビルド依存のみインストール
RUN apt-get update && apt-get install -y --no-install-recommends \
gcc libpq-dev \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt .
RUN pip install --no-cache-dir --user -r requirements.txt
# ── ステージ2: 本番ランタイム ──
FROM python:3.12-slim AS runtime
# セキュリティ: 非rootユーザー作成
RUN useradd --create-home --shell /bin/bash appuser
WORKDIR /home/appuser/app
# ビルドステージの site-packages のみコピー
COPY --from=builder /root/.local /home/appuser/.local
# アプリコードをコピー
COPY --chown=appuser:appuser app/ ./app/
USER appuser
# PATH に .local/bin を追加
ENV PATH=/home/appuser/.local/bin:$PATH
# ヘルスチェック
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD python -c "import httpx; httpx.get('http://localhost:8000/health')" || exit 1
EXPOSE 8000
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "4"]
docker-compose.yml(開発環境)
# docker-compose.yml
version: "3.9"
services:
api:
build: .
ports:
- "8000:8000"
environment:
- DATABASE_URL=postgresql+asyncpg://postgres:password@db:5432/appdb
- SECRET_KEY=dev-secret-key-change-in-production
- DEBUG=true
depends_on:
db:
condition: service_healthy
volumes:
- ./app:/home/appuser/app/app # ホットリロード用
db:
image: postgres:16-alpine
environment:
POSTGRES_DB: appdb
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
volumes:
- postgres_data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
interval: 5s
timeout: 5s
retries: 5
volumes:
postgres_data:
# 開発環境の起動
docker compose up -d
# API の動作確認
curl http://localhost:8000/docs
# → Swagger UI が表示されれば成功
# ログ確認
docker compose logs -f api
Step 7: GitHub Actions CI/CD パイプライン
Claude Code HTTP Hooks × GitHub Actions 完全統合ガイドでも解説されていますが、ここでは FastAPI 特有の CI/CD 設定を紹介します。
.github/workflows/ci.yml
name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.12"]
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: "pip"
- name: Install dependencies
run: |
pip install -r requirements.txt
pip install pytest pytest-asyncio pytest-cov httpx aiosqlite
- name: Run linting (ruff)
run: ruff check app/ tests/
- name: Run type checking (mypy)
run: mypy app/
- name: Run tests with coverage
run: |
pytest tests/ --cov=app --cov-report=xml --cov-fail-under=80
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v4
deploy:
needs: test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- uses: actions/checkout@v4
- name: Deploy to Fly.io
uses: superfly/flyctl-actions/setup-flyctl@master
- run: flyctl deploy --remote-only
env:
FLY_API_TOKEN: ${{ secrets.FLY_API_TOKEN }}
このパイプラインは、PR ごとに自動でテスト・型チェック・Lint を実行し、main ブランチへのマージ後に自動デプロイします。カバレッジが 80% を下回るとデプロイが失敗する設定になっており、品質の維持が自動化されます。
Step 8: マルチエージェントでコードレビューを自動化する
Claude Code のマルチエージェント機能実践ガイドの応用として、FastAPI 開発ではレビューエージェントを別プロセスで動かす設計が効果的です。
レビューエージェントの設定
.claude/review_agent.md を作成します。
# FastAPI コードレビュー専門エージェント
あなたは FastAPI のセキュリティ・パフォーマンス専門のコードレビュアーです。
## レビュー観点(優先度順)
1. **セキュリティ**: SQLインジェクション・認証バイパス・権限昇格のリスク
2. **N+1問題**: SELECT文のループ実行(selectinload/joinedload の提案)
3. **型安全性**: Pydantic バリデーションの抜け漏れ
4. **非同期安全性**: sync ブロッキング処理の混入(requests → httpx 等)
5. **エラーハンドリング**: 適切な HTTPException の使用
## レビュー出力形式
各問題について:
- 場所: ファイル名:行番号
- 重大度: CRITICAL / WARNING / INFO
- 問題: 何が問題か
- 修正案: 具体的なコード例付き
Claude Code のマルチエージェント機能でこのエージェントをサブエージェントとして起動することで、メインの開発セッションと並行してコードレビューを継続的に行えます。
本番デプロイ前チェックリスト
Fly.io や Railway へのデプロイ前に、以下を必ず確認します。
セキュリティ
SECRET_KEY が 256bit 以上のランダム文字列(openssl rand -hex 32 で生成)
ALLOWED_ORIGINS が本番ドメインのみに限定されている
- デバッグモード(
DEBUG=false)になっている
- 依存パッケージに既知の脆弱性がない(
pip audit で確認)
パフォーマンス
- データベース接続プールサイズが適切(本番:
pool_size=20)
- 重い処理が
BackgroundTasks または Celery で非同期化されている
- N+1 クエリがない(SQLAlchemy の
echo=True でログ確認)
可観測性
- 構造化ログが設定されている(
structlog 推奨)
/health エンドポイントが DB 接続確認を含んでいる
- Sentry や Datadog 等のエラー追跡が設定されている
Fly.io へのデプロイは以下のコマンドで完結します。
# Fly.io の初期設定(初回のみ)
fly launch --name my-fastapi-app --region nrt
# 環境変数の設定
fly secrets set SECRET_KEY="$(openssl rand -hex 32)"
fly secrets set DATABASE_URL="postgresql+asyncpg://..."
# デプロイ実行
fly deploy
# デプロイ確認
fly status
fly logs
まとめ
ここで扱うのはClaude Code × Python FastAPI の本番開発ワークフローを、設計から CI/CD デプロイまで完全に解説しました。
重要なポイントを振り返ります。
- CLAUDE.md が最重要: 技術スタック・コーディング規約・禁止事項を明記することで、生成されるコードの品質が大幅に向上する
- 段階的に要件を伝える: 一度に全部を生成させるより、ステップごとに指示する方が精度が高い
- テストは同時生成: エンドポイントと pytest テストを同時に依頼することで、後から書く手間が省ける
- Hooks で自動化: ファイル保存のたびにテストを自動実行することで、品質を継続的に維持できる
Claude Code は単なるコード生成ツールではなく、設計判断・エラー修正・テスト作成まで担える真のペアプログラマーです。この記事で紹介したパターンを組み合わせることで、個人開発者でも本番品質の FastAPI サーバーを短期間で構築できます。
Python バックエンド開発の実践的な知識