第 07 章「Webを作る ── HTML+CSS+JavaScriptという原点回帰」の 2 番目の角度: 動的処理は FastAPI で最小限(章本文の主張そのもの)。
章のどの主張に対応するか
動的処理はサーバー側で書く。Python(FastAPI)だけでいいのでは。 それも、最小限にする。
(章本文より)
example-1 は静的サイト(Markdown → HTML)、これは 書き込み・集計が 必要な業務側 を最小サイズで実演する。
やること
app.py(70 行) で受注 API を実装:GET /— ヘルスチェックPOST /orders— 受注登録(JSON)GET /orders— 一覧GET /orders/{id}— 1 件取得(404 つき)GET /stats— 集計(件数 / 売上 / 商品別)GET /docs— Swagger UI 自動生成(無料)
test_api.pyで 11 リクエストを叩いて結果を JSON に保存- ストレージは SQLite 1 ファイル(別 DB サーバ不要)
- Pydantic で 自動バリデーション(qty > 0、unit_price ≤ 10M、など)
構成
example-2/
├── README.md
├── app.py ── FastAPI アプリ本体(約 70 行)
├── test_api.py ── TestClient で 11 リクエスト走らせる
├── Makefile
├── results.md
└── out/
├── orders.db ── SQLite データ(.gitignore してもよい)
├── orders.json ── 一覧の応答
├── stats.json ── 集計の応答
├── openapi.json ── Swagger スキーマ(API ドキュメント)
└── request-log.txt── 11 件のレイテンシ
実行
pip install fastapi uvicorn httpx
make all # テスト走らせて結果を out/ に保存
make serve # http://localhost:8000 で立てる(Swagger は /docs)
なぜこれが「実例」になるのか
業務 Web に必要なのは:
- 画面(章 07 example-1 の Markdown → HTML)
- 書き込み・集計(このフォルダ:FastAPI + SQLite)
- 認証(必要なら、別途 30 行追加)
これだけ。React も Next.js も Postgres も Redis も要らない。
| 項目 | 大きいスタック | このスタック |
|---|---|---|
| バックエンド | Express + TypeScript | FastAPI 1 ファイル 70 行 |
| DB | Postgres + Redis + ORM | SQLite 1 ファイル |
| API ドキュメント | Swagger UI を別途設定 | /docs で自動生成 |
| バリデーション | zod / class-validator | Pydantic 自動 |
| 開発サーバ | nodemon + tsx + ts-node | uvicorn --reload |
| デプロイ | Docker + k8s + ロードバランサ | uvicorn app:app または fly.io |
これが章で言う「Python(FastAPI)だけ、それも最小限」の具体形。
自動生成された API ドキュメント
make serve してブラウザで http://localhost:8000/docs を開くと:
- 全エンドポイントの一覧
- リクエスト例 + 「Try it out」ボタン
- レスポンススキーマ
- バリデーションルール
これを コードを書いた瞬間に自動で得られる。Express で同じものを
作ろうとすると、swagger-jsdoc を入れて、デコレータを書いて、設定して...
最低 半日。FastAPI なら 0 秒(自動)。
バリデーション例
class OrderIn(BaseModel):
customer: str = Field(..., min_length=1, max_length=100)
item: str = Field(..., min_length=1, max_length=100)
qty: int = Field(..., gt=0, le=10_000)
unit_price: int = Field(..., gt=0, le=10_000_000)
不正なリクエストには 422 + エラー詳細 が返る:
POST /orders {"customer":"","item":"x","qty":-1,"unit_price":0}
→ 422 Unprocessable Entity
{
"detail": [
{"loc":["body","customer"],"msg":"String should have at least 1 character"},
{"loc":["body","qty"],"msg":"Input should be greater than 0"},
{"loc":["body","unit_price"],"msg":"Input should be greater than 0"}
]
}
これも 自動。手書きのバリデーション関数は 1 行も書いていない。
計測結果 — 第 07 章 example-2
実行環境: Linux 6.18 / FastAPI 0.136 / Pydantic 2.13 / SQLite 3.45 / Python 3.11
API レイテンシ(11 リクエスト)
GET / → 200 (10.3 ms)
POST /orders → 201 ( 5.3 ms)
POST /orders → 201 ( 4.3 ms)
POST /orders → 201 ( 4.3 ms)
POST /orders → 201 ( 4.2 ms)
POST /orders → 201 ( 4.0 ms)
GET /orders → 200 ( 3.2 ms)
GET /orders/5 → 200 ( 3.2 ms)
GET /orders/9999 → 404 ( 3.0 ms)
GET /stats → 200 ( 4.4 ms)
GET /openapi.json → 200 (26.2 ms)
| 操作 | 平均レイテンシ |
|---|---|
| 単純 GET | 約 3〜4 ms |
| POST(SQLite 書き込み + バリデーション) | 約 4〜5 ms |
| 集計クエリ(GROUP BY) | 約 4 ms |
| Swagger スキーマ生成 | 約 26 ms(初回のみ) |
10 ms 未満で応答。SQLite は単一マシンの業務 API には十分 速い。
規模
| 項目 | 数値 |
|---|---|
app.py 行数 |
約 70 行(コメント込み) |
| 依存パッケージ | fastapi, uvicorn, pydantic(計 3 個) |
node_modules |
0 KB |
| データベースサーバ | 不要(SQLite はファイル) |
| 起動時間 | < 1 秒 |
| メモリ | 約 50 MB |
集計結果(GET /stats)
5 件の受注を投入したあと:
{
"count": 5,
"revenue": 71400,
"by_item": [
{"item": "キャベツ", "count": 3, "revenue": 32400},
{"item": "トマト", "count": 1, "revenue": 20000},
{"item": "玉葱", "count": 1, "revenue": 19000}
]
}
集計は SQL の GROUP BY 1 行:
SELECT item, COUNT(*), SUM(total) FROM orders GROUP BY item ORDER BY SUM(total) DESC
ORM もデコレータも要らない。
バリデーション(自動)
class OrderIn(BaseModel):
customer: str = Field(..., min_length=1, max_length=100)
qty: int = Field(..., gt=0, le=10_000)
unit_price: int = Field(..., gt=0, le=10_000_000)
これだけで、不正リクエストは 422 で弾かれる。手書きの if 文ゼロ。
OpenAPI スキーマ(out/openapi.json、6.7 KB)
/docs で Swagger UI、/redoc で ReDoc ── 両方とも自動生成。
チームへの API 仕様共有が「URL を渡す」で終わる。
デプロイ(参考)
# 開発
uvicorn app:app --reload
# 本番(systemd)
ExecStart=/usr/bin/uvicorn app:app --host 0.0.0.0 --port 8000
# fly.io
fly launch --image python:3.11
fly deploy
月 0〜数百円で本番運用できる。Postgres も Redis も要らない規模なら SQLite + Cloudflare Tunnel で月 0 円も可能。
章本文との対応
動的処理はサーバ側で書く。Python(FastAPI)だけでいいのでは。 それも、最小限にする。
このフォルダは:
- 70 行の単一ファイル(
app.py) - 3 つの依存パッケージ(fastapi, uvicorn, pydantic)
- DB サーバなし(SQLite)
- Swagger 自動
「最小限」がどこまで小さくできるかの実演。
再現手順
pip install fastapi uvicorn httpx
make clean && make all # TestClient で動作確認
# または
make serve # http://localhost:8000/docs