開発

FlaskとFastAPIの違い|同じAPIを両方で書いて型検証・非同期・OpenAPIを比較

FlaskとFastAPIの違い|同じAPIを両方で書いて型検証・非同期・OpenAPIを比較

FlaskとFastAPIの違いは「FastAPIのほうが速い」の一言で片づけられがちですが、実装の現場で差が出るのは速度より、入力検証を誰が書くか、仕様書がどこから生まれるか、非同期にしたとき何が変わらないか、の3か所です。この記事では、同じ受注登録APIをFlask 3.1.3とFastAPI 0.162.5で書き、不正なデータを送ったときの応答、OpenAPIの出し方、async defとdefの動き方をコードと公式ドキュメントで比べます。最後に、どちらを採用するかを案件の条件から言い切ります。

まとめ:FlaskとFastAPIの違いを同一APIの実装差から先に言い切る

結論を先に置きます。JSONを返すAPIが主役で、入力の型と必須項目が多い案件ならFastAPIを選びます。Pydanticのモデルを1つ書けば、検証・エラー応答・OpenAPIの仕様書が同じ定義から出てくるためです。Flaskで同じ状態を作るには、検証の手書きか、APIFlaskやflask-smorestといった追加の部品が要ります。

非同期については、両者の差は「対応しているか」ではなく「1リクエストがワーカーをどう使うか」にあります。Flask公式はasyncビューでも1リクエストが1ワーカーを占有すると明記しており、同時に捌ける数は増えません。FastAPIはASGIのイベントループ上で動くため、待ち時間の長い外部呼び出しが多いAPIで差が出ます。

一方で、HTMLテンプレートを返す画面が中心のアプリや、既に安定稼働しているFlaskのAPIを、流行だけを理由に書き換える判断は採りません。FastAPIは2026年11月時点でも0系で、公式がMINOR更新で破壊的変更が入りうると明記しています。バージョン固定とテストを運用に組み込めない体制なら、Flaskのままのほうが保守は安定します。

同じ受注登録APIをFlask 3.1とFastAPI 0.162で書いて差を確かめる

比較の題材は、商品コードskuと数量qtyを受け取り、登録結果を返すだけのAPIです。機能を絞ることで、フレームワークが肩代わりする部分と開発者が書く部分の境界が見えます。

検証環境の前提とPythonと依存パッケージの版を揃えて入れる手順

2026年11月13日時点のPyPIで、Flaskの最新は3.1.3(2026年2月19日公開)、FastAPIの最新は0.162.5(2026年11月11日公開)です。動作要件はFlaskがPython 3.9以上、FastAPIがPython 3.10以上で、両方を同じ環境で試すならPython 3.10以上に揃えます。Flask単体の導入でつまずいた場合はvenv作成からエラー対処までのFlask導入手順を参照してください。

# 仮想環境を作り、比較に使う2つのフレームワークを入れる
python -m venv .venv
source .venv/bin/activate        # Windowsは .venv\Scripts\activate
pip install "flask==3.1.3" "fastapi[standard]==0.162.5"
python -c "import flask, fastapi, pydantic; print(flask.__version__, fastapi.__version__, pydantic.VERSION)"

FastAPIの[standard]を付けると、開発サーバーのfastapi devコマンドとASGIサーバーのUvicornが一緒に入ります。Flaskには開発用サーバーが本体に含まれているため、追加は不要です。

Flask 3.1で受注登録APIを書くと入力チェックが手書きになる箇所

Flaskのrequest.get_json()が返すのは、ただのPythonの辞書です。キーがあるか、型が合っているか、値の範囲は正しいかは、ビュー関数の中で開発者が確かめます。

# app_flask.py(Flask 3.1.3)
from flask import Flask, request, jsonify

app = Flask(__name__)

@app.post("/orders")
def create_order():
    data = request.get_json(silent=True)
    if not isinstance(data, dict):
        return jsonify(error="JSONオブジェクトで送ってください"), 400
    sku = data.get("sku")
    qty = data.get("qty")
    if not isinstance(sku, str) or not sku:
        return jsonify(error="skuは1文字以上の文字列です"), 400
    if not isinstance(qty, int) or isinstance(qty, bool) or qty < 1:
        return jsonify(error="qtyは1以上の整数です"), 400
    return jsonify(sku=sku, qty=qty), 201

項目が2つでも検証は6行になりました。boolを弾く行は、PythonでTrueがintの一種として扱われるために要るもので、手書きの検証ではこうした抜けが起きやすくなります。項目が20を超える業務APIでは、この部分がビュー関数の大半を占めます。

FastAPI 0.162で同じAPIを書くとPydanticモデルが引き受ける範囲

FastAPIでは、受け取る形をPydanticのモデルとして宣言し、引数の型注釈に置きます。FastAPI公式のRequest Bodyの章のとおり、本文のJSON読み取り・型の検証・エラー応答・ドキュメントへの反映までが、このモデル定義から行われます。

# app_fastapi.py(FastAPI 0.162.5 / Pydantic 2.14.3)
from fastapi import FastAPI
from pydantic import BaseModel, Field

app = FastAPI()

class Order(BaseModel):
    sku: str = Field(min_length=1)
    qty: int = Field(ge=1)

@app.post("/orders", status_code=201)
def create_order(order: Order) -> Order:
    return order

起動はfastapi dev app_fastapi.py、Flask側はflask --app app_flask runです。ビュー関数の中に検証の行はありません。Fieldの書き方やmodel_configでの挙動の切り替えはBaseModelの使い方とV1との違いを扱うPydanticの解説にまとめています。

型検証とエラー応答の差をcurlで不正なデータを送って確かめる

検証の手間よりも運用で効いてくるのは、エラーのときに何が返るかです。同じ不正データを両方へ送ります。

不正な数量を送ったときのFlaskの400応答とFastAPIの422応答

# 数量に文字列を入れて送る(Flaskは5000番、FastAPIは8000番で起動)
curl -s -X POST http://127.0.0.1:5000/orders -H "Content-Type: application/json" -d '{"sku":"A-1","qty":"abc"}'
# {"error":"qtyは1以上の整数です"}

curl -s -X POST http://127.0.0.1:8000/orders -H "Content-Type: application/json" -d '{"sku":"A-1","qty":"abc"}'
# {"detail":[{"type":"int_parsing","loc":["body","qty"],
#   "msg":"Input should be a valid integer, unable to parse string as an integer","input":"abc"}]}

Flaskの応答は、開発者が書いた文言とステータス400です。応答の形式は、コードを書いた人ごとに異なるものです。FastAPIはステータス422で、detailの配列に項目の位置loc・種別type・説明msgが入ります。FastAPI公式のHandling Errorsの章は、不正なデータを受けると内部でRequestValidationErrorを送出し、既定のハンドラがこの形で返すと説明しています。複数項目が同時に誤っていれば、配列にすべて並ぶ仕様です。

見落としやすい差がもう1つあります。"qty":"3"のように数字の文字列を送ると、上のFlaskのコードは拒否し、FastAPIは整数の3に変換して受け付けます。Pydanticの既定は型変換を許す緩いモードだからです。会計や在庫のように文字列の数値を拒否したい項目は、Field(strict=True)で厳格にします。

422応答の形式を業務APIの取り決めに合わせて上書きする際の注意点

既存の取引先システムが「エラーは400で、codeとmessageの形」と決めている場合は、@app.exception_handler(RequestValidationError)で応答を差し替えます。公式の同じ章は、例外オブジェクトにはファイル名や行番号の情報が含まれるため、文字列化してそのまま返すとシステム内部の情報が漏れうると注意しています。exc.errors()から項目ごとにlocとmsgを取り出し、必要な情報だけを詰め直す設計です。

Flaskでは、この共通化を最初から自分で設計します。errorhandlerで例外を受けて形式を揃える仕組みは作れますが、検証そのものが各ビューに散っている状態だと、形式の統一は規約とレビューで守るしかありません。

OpenAPIとSwagger UIをFlask側で揃えるために要る追加の部品

APIを外部へ渡す受託案件では、仕様書の鮮度が後工程の手戻りを左右します。コードと仕様書が同じ定義から出るかどうかが分かれ目です。

FastAPIが起動しただけで仕様書と画面を出す仕組みと本番での扱い

先ほどのFastAPIのコードを起動すると、/openapi.jsonにOpenAPI形式の仕様書が、/docsにSwagger UIの画面が、/redocにReDocの画面が出ます。Orderモデルのmin_length=1やge=1は、仕様書のminLengthやminimumに反映される定義です。OpenAPIという仕様そのものとSwaggerとの関係は仕様とツールの違いから整理したOpenAPIの解説で扱っています。

本番では、社外に仕様を公開しない限りこれらの画面を閉じます。FastAPI(docs_url=None, redoc_url=None, openapi_url=None)で3つとも止まります。閉じ方と、認証付きで残す方法は本番でSwagger UIを隠す手順に手順をまとめました。

FlaskでAPIFlaskやflask-smorestを入れて仕様書を出す場合の選び方

Flaskで同じ状態を作る主な選択肢は2つです。2026年11月13日時点のPyPIの版と合わせて並べます。

部品 最新版(公開日) スキーマ 向く場面
APIFlask 3.1.3(2026-11-02) marshmallowとPydantic Pydanticで型を揃えたい新規API
flask-smorest 0.47.0(2026-03-22) marshmallow 既存のmarshmallow資産があるAPI

APIFlaskの公式サイトは、自らを「Flaskの薄いラッパー」と説明し、marshmallowとPydanticの両方をスキーマアダプタで扱えるとしています。既存のFlaskアプリへ足すなら、FlaskをAPIFlaskに置き換える範囲で済むため、移行の負担はFastAPIへ書き直すより小さくなります。ただし部品が1つ増えるぶん、Flask本体とその部品の両方の更新を追う体制は必要です。

同期と非同期の動き方の違いをワーカーの占有と処理の並び方で比べる観点

「FastAPIは非同期で速い」という説明は、半分だけ正確です。両方の公式ドキュメントの記述から、どの条件で差が出るかを確かめます。

Flaskのasync defビューが1リクエスト1ワーカーを占有し続ける仕様

Flaskは2.0からasync defのビューを書けるようになりました。利用には、pip install flask[async]による追加の部品のインストールが必要です。ただしFlask公式のUsing async and awaitの章は、WSGIアプリとして1つのワーカーが1つのリクエストを処理し、asyncビューの場合はスレッド内でイベントループを起こしてビューを実行すると説明しています。そのうえで「Each request still ties up one worker, even for async views.」と明記し、「Async is not inherently faster than sync code.」とも書いています。

得られるのは、1つのビューの中で外部APIを3本同時に呼ぶといった並行処理です。同時に受けられるリクエスト数は増えません。さらに、ビューの完了と同時にイベントループが止まるため、asyncio.create_taskで起こした処理は取り消されます。背景処理をどうしても動かしたい場合、公式はasgirefのWsgiToAsgiアダプタでASGIサーバーに載せる方法を案内していますが、背景処理はタスクキューに渡すのが基本の設計です。

FastAPIでdefとasync defを取り違えたときにイベントループが止まる失敗

FastAPIはStarletteの上に作られたASGIアプリで、Uvicornのイベントループ上で動きます。FastAPI公式のConcurrency and async / awaitの章によれば、defで書いたパス関数は外部のスレッドプールで実行されて待ち合わせられ、async defで書いた関数はイベントループ上で直接呼ばれます。依存関数も同じ扱いです。

失敗の典型は、async defの中でrequests.getや同期版のDBドライバを呼ぶ書き方です。この呼び出しはイベントループそのものを止めるため、その間は他のリクエストも進みません。Flaskから移しただけでdefを機械的にasync defへ書き換えると、この状態になります。公式は迷ったらdefで書くよう勧めています(原文「If you just don’t know, use normal def.」)。awaitで呼べるライブラリだけで処理が完結する場合に限ってasync defにします。サーバー側のワーカー数やプロセス構成はGunicornとの違いと本番設定を扱うUvicornの解説を参照してください。

FlaskとFastAPIのどちらを採用するか案件の条件から言い切る判断

ここまでの差を、選ぶための条件に落とします。性能の評判ではなく、要件と保守体制で判定します。

FastAPIを選ぶ条件と0系のバージョン固定を前提にした保守の段取り

FastAPIを選ぶのは、JSONを返すAPIが主体で、受け取る項目が多く、仕様書を外部と共有する案件です。待ち時間の長い外部APIを束ねる中継APIも向いています。ただし採用と同時に決めるべきなのは、バージョン固定の運用です。FastAPI公式のAbout FastAPI versionsの章は、0系であるためMINOR更新で破壊的変更が入りうること、PATCH更新はバグ修正のみであることを明記し、MINORの範囲で固定する書き方を例示しています。

# requirements.txt(0.162系のPATCH更新だけを受け入れる)
fastapi[standard]>=0.162.5,<0.163.0
pydantic>=2.14,<3.0
# starletteはFastAPIが対応版を指定するため固定しない(公式の推奨)

MINORを上げるときは、テストを流してから固定値を書き換えます。テストのない案件でFastAPIを入れると、更新のたびに手で動作確認する負担が残る運用です。ディレクトリ構成とテストの組み方はディレクトリ構成とテストを実測で解説したFastAPIのベストプラクティスが出発点になります。

Flaskのまま続ける条件と移行しても得るものが少ない場面の見極め

Flaskを続けるのは、次のいずれかに当てはまる場合です。Jinja2のテンプレートでHTMLを返す画面が中心である。APIの項目が少なく、仕様書を外部に渡さない。Flask-Loginなど既存の拡張に依存した処理が多く、置き換え先が無い。この3つでは、FastAPIへ移しても検証と仕様書の自動化という利点がほとんど効きません。

Flask本体は2026年11月時点で3.1系が最新で、3.1.3は2026年2月にセッションまわりの修正として出ています。FastAPIのような0系のMINOR更新を追う負担はありません。管理画面やORMまで含めた比較が必要なら、内蔵範囲と案件規模から選ぶDjangoとFlaskの違いでDjangoを含めた判断を扱っています。

Flaskから段階的に移す場合にエンドポイント単位で切り替える進め方

移行を決めた場合でも、全面の書き直しを避ける方針です。手書きの検証が膨らんだエンドポイントから順に、FastAPIの別サービスとして立て、リバースプロキシのパス振り分けで1本ずつ切り替えます。切り替え前後で同じ入力を送り、応答を突き合わせるテストを先に用意しておくと、文字列の数値を受け付けるかどうかといった前章の差を事前に拾えます。

エラー応答の形式が変わる点は、呼び出し側のシステムへの影響として必ず洗い出します。どのエンドポイントから移すか、既存の連携先との取り決めをどう守るかの設計から関わる場合は、API開発・システム連携の受託開発で相談を受けています。

よくある質問

FlaskとFastAPIの比較で検索されることの多い質問に、2026年11月時点の版で答えます。

FastAPIはFlaskより処理が速いのですか?

待ち時間の長い外部呼び出しが多いAPIをasync defとawait対応のライブラリで書いた場合は、同時に捌ける数で差が出ます。一方、CPUを使う計算やDB問い合わせが支配的な処理では、フレームワーク本体による差は小さいものです。async defの中で同期の処理を呼ぶと、かえって遅くなる点にも注意が要ります。

FlaskとFastAPIはどちらが初心者に向いていますか?

Webの仕組みから順に理解したいならFlaskです。リクエストを受けて辞書を取り出し、自分で検証する流れが見えます。APIを作ることが目的で、Pythonの型ヒントに抵抗がなければFastAPIのほうが早く形になります。入力検証と仕様書の作成を書かずに済むためです。

FlaskのアプリをFastAPIに移行するべきですか?

手書きの入力検証とSwaggerの定義が積み上がっているAPIなら、移行の効果が出ます。HTMLテンプレートを返す画面が中心のアプリなら、得られる利点は小さいという判断です。移す場合も全面の書き直しではなく、エンドポイント単位で切り替える進め方を採ります。

FlaskでもPydanticを使って入力を検証できますか?

FlaskでもPydanticによる入力検証は可能です。ビューの中でOrder.model_validate(request.get_json())のように呼べば、検証そのものはFastAPIと同じPydanticが行います。ただしエラー応答の形式や仕様書への反映は自分で組むことになります。そこまで揃えたい場合は、Pydanticに対応したAPIFlaskを入れるのが近道です。

FastAPIの本番環境はどのサーバーで動かしますか?

ASGIサーバーのUvicornで動かします。2026年11月時点の最新は0.54.0です。fastapi runコマンドも内部でUvicornを使います。Flaskは同期のWSGIアプリのため、GunicornなどのWSGIサーバーで動かすのが基本で、ここも両者の構成の違いになります。

関連記事

お気に入りに入れた記事の一覧

一創の公式 SNS

X では毎朝の新着記事と IT ニュースの速報を、YouTube・TikTok・Instagram・LinkedIn・Facebook では記事の解説動画を配信しています。

この記事は以下の記事からリンクされています

資料請求

RELATED POSTS 関連記事

目次