はじめに
前の記事でFlaskの基本を触った。
「結局どっちを使えばいいの?」という疑問が残ったので、もう少し深掘りして整理した。同期/非同期・用途・エコシステムの違いを軸に、実際に両方触ってみた上での個人的な判断基準をまとめる。
前提 — 2つのフレームワークの立ち位置
まず歴史的な背景を整理する。
Flask(2010年〜)
Armin Ronacher が作った「マイクロフレームワーク」。必要最小限の機能だけを提供して、あとは自分で選んで組み合わせる設計思想。14年の歴史があり、エコシステムが成熟している。
FastAPI(2018年〜)
Sebastián Ramírez が作ったAPI特化フレームワーク。Python 3.6以降の型ヒントを最大限活用する設計。Starletteというフレームワークの上に構築されていて、非同期処理がネイティブで動く。
PHPで言うとFlaskがSlim Framework、FastAPIがLaravel(API部分に絞ったもの)に近いイメージ。
同期と非同期の違い
一番根本的な違いがここ。
Flaskの同期モデル
# Flask — デフォルトは同期
import requests
from flask import Flask
app = Flask(__name__)
@app.route("/dashboard")
def dashboard():
# これはブロッキング処理
user = requests.get("https://api.example.com/user/1").json()
order = requests.get("https://api.example.com/orders").json()
return {"user": user, "order": order}
# user取得が終わるまでorder取得は始まらない → 合計待ち時間 = 直列
Flask 2.0以降でasync defも書けるようになったが、内部でスレッドを使う実装になっていて、asyncioのネイティブ非同期とは異なる。
# Flask 2.0以降 — async defは書けるが本物の非同期ではない
@app.route("/dashboard")
async def dashboard():
# asyncioを使いたいなら別途設定が必要
...
FastAPIの非同期モデル
# FastAPI — ネイティブ非同期
import httpx
from fastapi import FastAPI
app = FastAPI()
@app.get("/dashboard")
async def dashboard():
async with httpx.AsyncClient() as client:
user_task = client.get("https://api.example.com/user/1")
order_task = client.get("https://api.example.com/orders")
# 2つのリクエストを同時に実行
user_res, order_res = await asyncio.gather(user_task, order_task)
return {"user": user_res.json(), "order": order_res.json()}
# 合計待ち時間 = max(user取得, order取得) → 並列で速い
APIが複数の外部サービスを叩くような構成では、FastAPIの非同期が効果を発揮する。
バリデーションの違い
前の記事でも触れたが、使い分けに直結するので改めて整理。
Flaskのバリデーション
標準では何もない。自前で書くかライブラリを選ぶ。
# 自前でバリデーション
from flask import request, jsonify
@app.route("/users", methods=["POST"])
def create_user():
data = request.get_json()
# 手動バリデーション(これが地味に面倒)
if not data.get("name"):
return jsonify({"error": "nameは必須です"}), 422
if len(data["name"]) > 50:
return jsonify({"error": "nameは50文字以内です"}), 422
if not data.get("email"):
return jsonify({"error": "emailは必須です"}), 422
return jsonify({"name": data["name"]}), 201
# marshmallowを使う場合
from marshmallow import Schema, fields, validate, ValidationError
class UserSchema(Schema):
name = fields.Str(required=True, validate=validate.Length(max=50))
email = fields.Email(required=True)
schema = UserSchema()
@app.route("/users", methods=["POST"])
def create_user():
try:
data = schema.load(request.get_json())
except ValidationError as e:
return jsonify({"errors": e.messages}), 422
return jsonify({"name": data["name"]}), 201
FastAPIのバリデーション
Pydanticが標準で入っている。
from fastapi import FastAPI
from pydantic import BaseModel, Field, EmailStr
app = FastAPI()
class UserCreate(BaseModel):
name: str = Field(..., max_length=50)
email: EmailStr
@app.post("/users", status_code=201)
def create_user(user: UserCreate):
return {"name": user.name}
# バリデーションエラーは自動で422レスポンスになる
コード量と可読性の差がある。バリデーションが多いAPIを作るほどFastAPIの優位性が出る。
ドキュメント生成の違い
Flask
デフォルトではSwagger UIが自動生成されない。flasggerやflask-openapi3を入れて設定する必要がある。
pip install flasgger
from flasgger import Swagger
app = Flask(__name__)
swagger = Swagger(app)
@app.route("/users/<int:user_id>")
def get_user(user_id):
"""
ユーザーを取得する
---
parameters:
- name: user_id
in: path
type: integer
responses:
200:
description: ユーザー情報
"""
return {"user_id": user_id}
docstringにYAMLを書くスタイルは正直つらい。
FastAPI
型ヒントを書くだけで/docsにSwagger UIが自動生成される。追加設定は不要。
@app.get("/users/{user_id}", summary="ユーザー取得", tags=["users"])
def get_user(user_id: int) -> UserResponse:
return {"user_id": user_id}
APIドキュメントの自動生成は、FastAPIを選ぶ理由として一番わかりやすいメリットだと感じている。
テンプレートエンジン(HTML出力)
Webアプリとして画面を返したい場合はFlaskのほうが向いている。
Flask + Jinja2
from flask import render_template
@app.route("/users")
def user_list():
users = [{"name": "田中"}, {"name": "鈴木"}]
return render_template("users.html", users=users)
<!-- templates/users.html -->
<!DOCTYPE html>
<html>
<body>
<ul>
{% for user in users %}
<li>{{ user.name }}</li>
{% endfor %}
</ul>
</body>
</html>
Jinja2はPHPのBladeに近い感覚で書ける。PHPエンジニアにはなじみやすい。
FastAPI
Jinja2Templatesを使えばHTMLも返せるが、設計としてはAPI(JSON)に特化している。フロントエンドをNext.jsなど別のフレームワークで作ってAPIとして使う構成のほうが自然。
from fastapi import FastAPI, Request
from fastapi.templating import Jinja2Templates
app = FastAPI()
templates = Jinja2Templates(directory="templates")
@app.get("/users")
def user_list(request: Request):
users = [{"name": "田中"}, {"name": "鈴木"}]
return templates.TemplateResponse(
"users.html",
{"request": request, "users": users},
)
できなくはないが、FastAPIでHTMLを返すのは少し不自然に感じる。
エコシステムの成熟度
Flaskのほうが歴史が長い分、拡張ライブラリが多い。
| 機能 | Flask拡張 | FastAPI相当 |
|---|---|---|
| ORM | Flask-SQLAlchemy | SQLAlchemy(直接) |
| 認証 | Flask-Login / Flask-JWT | python-jose / authlib |
| マイグレーション | Flask-Migrate(Alembic) | Alembic(直接) |
| メール送信 | Flask-Mail | fastapi-mail |
| キャッシュ | Flask-Caching | aiocache |
| 管理画面 | Flask-Admin | なし(自前か別途) |
| レート制限 | Flask-Limiter | slowapi |
管理画面(Flask-Admin)はFlaskの独壇場。Djangoのadminほど高機能ではないが、モデルをCRUDできる管理画面が数行で作れる。FastAPIにはこれに相当するものがない。
パフォーマンス
一般的なベンチマーク結果のイメージ(環境によって変わるので参考程度)。
リクエスト/秒(JSON APIの場合)
Flask(同期) : 約3,000〜5,000 req/s
Flask(async) : 約4,000〜6,000 req/s
FastAPI(sync def): 約5,000〜8,000 req/s
FastAPI(async def): 約8,000〜15,000 req/s
I/Oバウンドな処理(DB・外部API)では非同期の効果が大きく出る。CPUバウンドな処理だけなら差は小さい。
ただし「どちらも現代のサービスで十分な性能」ではある。ボトルネックがフレームワークになることはほぼない。
個人的な使い分け基準
整理するとこうなった。
Flaskを選ぶとき
✓ HTMLテンプレートを使うWebアプリを作る
✓ 管理画面(Flask-Admin)が必要
✓ 小さなスクリプトやプロトタイプ
✓ チームがFlaskに慣れている
✓ 既存のFlaskプロジェクトを引き継いだ
FastAPIを選ぶとき
✓ JSON APIをメインに作る
✓ 型安全を最初から担保したい
✓ Swagger UIの自動生成が必要
✓ 非同期処理が必要(複数外部API呼び出しなど)
✓ フロントエンドをNext.jsなどで分離する構成
✓ OpenAPIスキーマをフロントチームと共有する
どちらでもよいとき
- REST APIの規模が小さい(エンドポイントが10個以下)
- チームの慣れで決める
- 個人開発でスピード重視
移行コスト
もし途中でFlask→FastAPIに移行したくなったら。
ルーティングの基本的な書き方は似ているので、コントローラー層の書き直しはそこまで大変ではない。一番コストがかかるのはバリデーション部分で、marshmallowなどを使っていた場合はPydanticモデルへの書き換えが必要になる。
逆にFastAPI→Flaskへの移行はほぼない(非同期コードの書き直しが発生するので)。
まとめ
| 観点 | Flask | FastAPI |
|---|---|---|
| 向いている用途 | WebアプリAP・管理画面・小規模API | JSON API・型安全・非同期が必要な場合 |
| バリデーション | 別途ライブラリが必要 | Pydantic標準 |
| ドキュメント | 設定が必要 | 自動生成 |
| 非同期 | 限定的 | ネイティブ |
| 管理画面 | Flask-Adminが強力 | ほぼなし |
| テンプレート | Jinja2が自然 | やや不自然 |
| 学習コスト | 低い | 中くらい |
どちらが優れているという話ではなく、「何を作るか」で決まる。APIを作るなら今はFastAPIを選ぶ。HTMLも返す必要があるWebアプリやちょっとした管理ツールはFlaskが素直に書ける。
この判断基準を持ってから、フレームワーク選びで迷う時間がなくなった。