はい、承知いたしました。FastAPIに関する詳細な解説記事を約5000語で記述し、直接表示します。
FastAPIとは?Python最速APIフレームワークを徹底解説
導入:モダンなWeb API開発の波とFastAPIの登場
現代のソフトウェア開発において、Web API(Application Programming Interface)は不可欠な要素となっています。マイクロサービスアーキテクチャの普及、フロントエンドとバックエンドの分離(SPA, モバイルアプリ)、IoTデバイスとの連携など、様々なシステムがAPIを通じて相互に連携しています。Web APIは、異なる技術スタックで構築されたシステム間でのデータ交換や機能利用を可能にし、柔軟でスケーラブルなシステム構築を支えています。
Pythonは、その読みやすい構文、豊富なライブラリ、多様な用途から、Web開発においても非常に人気のある言語です。これまで、PythonでWeb APIやWebアプリケーションを開発するためのフレームワークとしては、DjangoやFlaskが広く利用されてきました。
- Django: フルスタックフレームワークとして、ORM(Object-Relational Mapper)、テンプレートエンジン、管理画面、認証システムなど、Web開発に必要な多くの機能を包括的に提供します。大規模なWebアプリケーション開発に適していますが、API開発に特化する場合はややオーバースペックに感じられることもあります。
- Flask: マイクロフレームワークとして、必要最小限の機能のみを提供し、それ以外の部分は開発者が自由にライブラリを選択して構築します。シンプルで柔軟性が高い反面、API開発に必要なデータ検証、シリアライゼーション、ドキュメント生成といった機能は別途実装するか、ライブラリを組み合わせて利用する必要があります。
これらのフレームワークは長年にわたりPythonコミュニティに貢献してきましたが、近年のWeb開発トレンド、特に非同期処理の重要性の高まりや、API仕様の標準化(OpenAPIなど)への対応において、新たなフレームワークが求められるようになりました。
そこに登場したのが、FastAPIです。
FastAPIは、Pythonの型ヒントを最大限に活用し、高いパフォーマンス、迅速な開発、自動的なAPIドキュメント生成を実現する、モダンなWeb APIフレームワークです。「Python最速」と謳われるそのパフォーマンスと、開発者の生産性を飛躍的に向上させる多くの特徴を備えています。
この記事では、FastAPIがなぜ注目されているのか、その核心にある技術(非同期処理、ASGI、Pydantic、Starlette)は何か、そして実際にFastAPIを使ってどのようにAPIを開発するのかを、コード例を交えながら徹底的に解説します。FastAPIの強力な機能(ルーティング、データ検証、依存性注入、セキュリティ、データベース連携、テストなど)を網羅し、読者がFastAPIを習得し、実際のプロジェクトに活用できるようになることを目指します。
さあ、PythonによるモダンなAPI開発の扉を開き、FastAPIの世界へ飛び込みましょう。
FastAPIの核心 – なぜFastAPIは「速い」のか?
FastAPIがその名の通り「速い」と言われるのには、いくつかの技術的な理由があります。これは単にコードの実行速度だけでなく、「開発速度」も含めた総合的な速さを指していますが、まずはランタイムパフォーマンスに焦点を当ててみましょう。
FastAPIのパフォーマンスの源泉は、主に以下の要素にあります。
- 非同期処理 (asyncio)
- ASGI (Asynchronous Server Gateway Interface)
- 高速なデータ検証・シリアライゼーション (Pydantic)
- 軽量な基盤フレームワーク (Starlette)
1. 非同期処理 (asyncio)
従来のPython Webフレームワークの多くは、WSGI (Web Server Gateway Interface) を基盤としていました。WSGIは同期的なインターフェースであり、リクエストごとにワーカープロセスやスレッドを割り当て、そのリクエストの処理が終わるまで待機するというモデルを採用しています。データベースへのアクセス、外部APIへのリクエスト、ファイルI/Oなど、時間のかかるI/O処理が発生すると、その間ワーカーはそのリクエストのためにブロックされ、他のリクエストを処理できませんでした。これは、多数の同時リクエストを効率的に捌く上でのボトルネックとなります。
一方、FastAPIはPython 3.5以降で導入されたasyncio
ライブラリを活用し、非同期処理をネイティブにサポートしています。asyncio
を使うことで、I/O処理が完了するのを待つ間に、他の処理(別のリクエストの処理など)に切り替えることができます。これにより、限られたリソース(ワーカープロセス/スレッド)でより多くの同時リクエストを、ノンブロッキングに効率よく処理することが可能になります。
FastAPIのエンドポイント関数は、async def
キーワードを使って非同期関数として定義できます。
“`python
from fastapi import FastAPI
app = FastAPI()
@app.get(“/items/{item_id}”)
async def read_item(item_id: int):
# データベースからのデータ取得など、非同期I/O処理をここで実行
# 例: await database.fetch_one(“SELECT * FROM items WHERE id = :id”, {“id”: item_id})
await asyncio.sleep(1) # 非同期処理の例(1秒待機)
return {“item_id”: item_id, “message”: “This is an asynchronous endpoint”}
“`
このようにasync def
で定義された関数内でawait
キーワードを使うことで、時間のかかるI/O処理中にCPUを解放し、他のタスクに切り替えることができます。これは、特に多くのI/Oバウンドな処理(データベースアクセス、外部APIコールなど)を含むAPIにおいては、スループットを劇的に向上させる鍵となります。
もちろん、FastAPIでは同期的なエンドポイント(def
で定義された関数)も記述できます。この場合、FastAPIは内部的にスレッドプールを使用してブロッキング処理を非同期的に実行し、ASGIの非同期特性を損なわないようにしますが、非同期対応しているライブラリ(例:asyncpg for PostgreSQL, aiohttp for HTTP requests)を使う場合は、エンドポイントもasync def
にしてawait
するのがベストプラクティスです。
2. ASGI (Asynchronous Server Gateway Interface)
WSGIがPythonのWebアプリケーションとWebサーバー間の同期的なインターフェースであったのに対し、ASGIは非同期処理をサポートするために設計された新しいインターフェースです。FastAPIはASGI上で動作します。
ASGIは、非同期リクエスト処理、WebSocket、HTTP/2などの新しいWeb技術に対応しています。FastAPI自体はASGIサーバーではありませんが、ASGIサーバー上で実行されるアプリケーションフレームワークです。
ASGIサーバーの代表例としては、UvicornやHypercornがあります。これらのサーバーは、asyncio
を最大限に活用し、非常に高速かつ非同期的に動作します。特にUvicornはRustのuvloopやh11といった高速なライブラリを利用しており、そのパフォーマンスは非常に高いです。
FastAPIアプリケーションは、UvicornのようなASGIサーバーを使ってデプロイ・実行されます。これにより、FastAPIアプリケーションはASGIサーバーの持つ非同期処理能力を最大限に引き出すことができます。
3. 高速なデータ検証・シリアライゼーション (Pydantic)
API開発では、クライアントから送られてくるリクエストデータ(JSONなど)の検証と、サーバーからクライアントへ返すレスポンスデータの整形(PythonオブジェクトからJSONへの変換など、シリアライゼーション)が非常に重要です。これらの処理は、セキュリティ(不正なデータの拒否)と信頼性(期待される形式のデータの保証)のために必須ですが、手作業で実装すると煩雑でバグの原因になりがちです。
FastAPIは、データ検証・シリアライゼーションライブラリとしてPydanticを深く統合しています。PydanticはPythonの型ヒントを活用してデータの構造を定義し、高速なデータ検証とシリアライゼーションを自動で行います。
“`python
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
from fastapi import FastAPI
app = FastAPI()
@app.post(“/items/”)
async def create_item(item: Item): # FastAPIが自動的にリクエストボディをItemモデルとして検証
return item # 検証済みのItemオブジェクトが自動的にJSONにシリアライズされて返される
“`
Pydanticは、以下の点でFastAPIの高速化と開発効率向上に貢献しています。
- 自動検証: 関数シグネチャの型ヒントに従って、受信したリクエストデータ(JSONなど)を自動的に検証します。型変換、必須フィールド、データ型チェックなどがPydanticによって高速に実行されます。無効なデータは自動的に422 Unprocessable Entityエラーとしてクライアントに返され、手動での検証コード記述が不要になります。
- 自動シリアライゼーション: Pythonオブジェクト(Pydanticモデルのインスタンスを含む)をレスポンスとして返す際、自動的にJSONなどの指定された形式にシリアライズします。
- OpenAPI/JSON Schema生成: Pydanticモデルの定義は、自動的にOpenAPIスキーマおよびJSON Schemaに変換されます。これは後述する自動ドキュメント生成の基盤となります。
- パフォーマンス: Pydanticは内部的に効率的なデータ処理を行っており、データ検証・変換が高速です。
Pydanticによる強力な型ベースのデータ処理は、開発者がデータ検証やシリアライゼーションの定型コードを書く必要をなくし、APIのビジネスロジックに集中できるようにします。これは「開発速度」における重要な貢献です。
4. 軽量な基盤フレームワーク (Starlette)
FastAPI自体は、Starletteという軽量なASGIフレームワークを基盤として構築されています。Starletteは、HTTPリクエストハンドリング、WebSocket、バックグラウンドタスク、起動・終了イベント、ミドルウェアなど、FastAPIの主要な機能を多く提供しています。
Starletteは意図的にシンプルかつ高性能に設計されており、必要最低限の機能に絞ることでオーバーヘッドを減らしています。FastAPIはStarletteの上に、Pydanticとの統合、依存性注入システム、自動ドキュメント生成といったAPI開発に特化した層を追加しています。
FastAPIはStarletteの堅牢で高速な基盤を継承しつつ、API開発に必要な機能を「全部入り」ではなく、よく使う標準技術(型ヒント、Pydantic、OpenAPI)と組み合わせることで、シンプルかつ強力なフレームワークとなっています。
これらの要素(非同期処理、ASGI、Pydantic、Starlette)が組み合わさることで、FastAPIは非常に高いパフォーマンスと開発効率を実現しています。特にI/OバウンドなAPIにおいて、その非同期処理能力は顕著な差を生み出します。
FastAPIの主要な特徴とメリット
FastAPIが単なる「速い」フレームワークに留まらない理由は、その設計思想と豊富な機能群にあります。開発者がモダンなAPIを効率的かつ堅牢に開発するために設計されており、多くのメリットを提供します。
1. 高速なパフォーマンス
前述の通り、非同期処理 (asyncio
) と高速なASGIサーバー (Uvicorn
)、そして高速なデータ処理 (Pydantic
) により、高いスループットと低いレイテンシを実現します。これは、大量の同時リクエストを処理する必要があるモダンなWebサービスやマイクロサービスにおいて大きな強みとなります。TechEmpowerなどのベンチマークでも、FastAPI + UvicornはPythonフレームワークの中で常にトップクラスのパフォーマンスを示しています。
2. 圧倒的な開発速度
FastAPIは、開発者がAPIのロジックに集中できるように、多くの定型的な作業を自動化・簡略化しています。
- 型ヒント: Python標準の型ヒントを利用することで、エディタのコード補完、型チェック、そしてFastAPIの自動データ検証・シリアライゼーションが強力に機能します。これにより、コード記述量が減り、型に関連するバグも早期に発見できます。
- 自動データ検証: Pydanticによるスキーマ定義と型ヒントにより、リクエストデータの検証コードをほとんど書く必要がありません。
- 自動ドキュメント生成: APIエンドポイントの定義から、インタラクティブなドキュメント(Swagger UI, ReDoc)が自動生成されます。ドキュメント作成・更新の手間が劇的に削減され、常に最新かつ正確なドキュメントが提供されます。
- 依存性注入システム: 共通処理(認証、データベース接続など)を簡単に再利用可能な依存性として定義・注入できます。コードの構造化とテスト容易性を高めます。
これらの機能により、開発者はAPIのコア機能を素早く実装し、市場投入までの時間を短縮できます。
3. 少ないコード量
Flaskなどのマイクロフレームワークと比較しても、FastAPIは同等の機能を実現するのに必要なコード量が少ない傾向があります。これは、Pydanticによるデータ定義と検証の自動化、そして依存性注入システムが大きく貢献しています。必要な記述は、関数の定義、型ヒント、そしてビジネスロジックのみというシンプルさです。
4. 堅牢性
- 自動データ検証: Pydanticによる厳格なデータ検証は、不正なデータや予期しない形式のデータがアプリケーションの内部ロジックに到達するのを防ぎます。これにより、実行時エラーやセキュリティ上の問題を未然に防ぐことができます。
- 型ヒント: 静的解析ツール(Mypyなど)と組み合わせることで、開発段階で多くの型関連のエラーを検出できます。
これらの機能は、APIの信頼性と安定性を向上させます。
5. 自動インタラクティブAPIドキュメント
FastAPIの最も魅力的な機能の一つが、API定義からの自動ドキュメント生成です。
- Swagger UI:
/docs
パスにアクセスすると、インタラクティブなAPIドキュメント(Swagger UI)が表示されます。各エンドポイントの詳細、パラメータ、レスポンススキーマが確認でき、ブラウザ上から実際にAPIリクエストを試すことも可能です。これはAPIのテストやクライアント開発において非常に便利です。 - ReDoc:
/redoc
パスにアクセスすると、別の形式のAPIドキュメント(ReDoc)が表示されます。こちらはよりドキュメントとしての読解性に優れています。
これらのドキュメントは、APIのコードを修正するたびに自動的に更新されるため、ドキュメントと実際のAPI定義が乖離する「ドキュメントの古さ問題」を防ぐことができます。これはOpenAPI (旧Swagger) 仕様に基づいています。
6. 標準技術の活用
FastAPIは、PythonコミュニティやWeb開発コミュニティで広く使われている標準技術を積極的に採用しています。
- Python型ヒント (PEP 484, PEP 526, PEP 593など): Python 3.5以降で導入された標準機能です。
- OpenAPI (旧Swagger): RESTful APIの標準的な記述仕様です。多くのツールやサービスがOpenAPIをサポートしており、FastAPIで生成されたドキュメントはこれらと互換性があります。
- JSON Schema: JSONデータの構造を記述するための標準仕様です。PydanticはJSON Schemaと互換性があります。
これらの標準技術を活用することで、FastAPIの学習コストが下がり、他のツールやライブラリとの連携が容易になります。
7. 強力な依存性注入システム
FastAPIの依存性注入(Dependency Injection, DI)システムは非常に強力で柔軟です。エンドポイント関数のパラメータとして依存性(他の関数、クラス、オブジェクトなど)を宣言することで、FastAPIが自動的にそれを解決し、関数に渡してくれます。
- 認証や認可のロジックを共通化する
- データベースセッションや設定値を共有する
- テスト時に依存性を簡単に差し替える
これらのユースケースをDIシステムを使ってエレガントに実装できます。yield
を使った依存性により、リソースのセットアップ(DB接続プール作成など)とクリーンアップ(接続解放など)を管理することも可能です。
8. 学習コスト
Flaskのようなマイクロフレームワークを使った経験があれば、FastAPIのルーティングや基本的な構造は非常に馴染みやすいでしょう。Pydanticと型ヒントの概念を理解すれば、すぐにFastAPIのメリットを享受できます。ドキュメントも非常に充実しており、具体的なコード例が豊富に掲載されています。
9. 豊富な機能
基本的なHTTPメソッドのルーティング、パス/クエリ/リクエストボディパラメータの処理に加え、FastAPIは以下のような機能も標準的、あるいは統合されたライブラリとして提供しています。
- セキュリティ機能 (OAuth2, JWTなど)
- ミドルウェアの利用・作成
- WebSocketサポート
- GraphQLサポート (Starlette経由、または統合ライブラリ利用)
- バックグラウンドタスク
- 起動・終了イベントハンドリング
これらの機能により、小規模なプロトタイプから大規模なエンタープライズアプリケーションまで、幅広いユースケースに対応できます。
FastAPIは、これらの特徴を組み合わせることで、単にAPIを構築するだけでなく、高品質で、メンテナンスしやすく、スケーラブルなAPIを迅速に開発するための強力なツールとなっています。
FastAPIを使ってみよう – 基本的なAPIの作成
それでは、実際にFastAPIを使って簡単なAPIを作成してみましょう。
1. 環境構築
まずはPython環境を準備します。仮想環境を使うのが一般的です。
“`bash
仮想環境の作成
python -m venv .venv
仮想環境の有効化
Windowsの場合:
.venv\Scripts\activate
macOS/Linuxの場合:
source .venv/bin/activate
“`
仮想環境を有効化したら、FastAPIとASGIサーバー(Uvicorn)をインストールします。
bash
pip install fastapi uvicorn[standard]
uvicorn[standard]
は、Uvicorn本体に加えて、標準でよく使われる依存ライブラリ(例: python-multipart
for forms/file uploads, jinja2
for templating, PyYAML
for OpenAPI YAML, httptools
and uvloop
for performance)も一緒にインストールします。
2. 最小限のAPIコード
main.py
というファイルを作成し、以下のコードを記述します。
“`python
main.py
from fastapi import FastAPI
FastAPIアプリケーションのインスタンスを作成
app = FastAPI()
ルートパス (“/”) へのGETリクエストに対するハンドラーを定義
@app.get(“/”)
def read_root():
return {“Hello”: “World”}
“/items/{item_id}” パスへのGETリクエストに対するハンドラーを定義
item_id はパスパラメータとして受け取る
@app.get(“/items/{item_id}”)
def read_item(item_id: int, q: str | None = None):
# item_id は自動的にint型に変換される
# q はオプショナルなクエリパラメータ (str型、デフォルト値はNone)
return {“item_id”: item_id, “q”: q}
“`
このコードは非常にシンプルです。
FastAPI
クラスをインポートし、app
インスタンスを作成します。これがアプリケーションの本体となります。@app.get("/")
デコレーターは、HTTPのGETメソッドで/
パスへのリクエストを受け取る関数を定義します。read_root()
関数は、リクエストに対してPythonの辞書を返します。FastAPIはこれを自動的にJSONレスポンスに変換します。@app.get("/items/{item_id}")
デコレーターは、/items/
の後に続く部分をパスパラメータとして受け取ります。波括弧{}
で囲まれた部分がパラメータ名になります。read_item(item_id: int, q: str | None = None)
関数は、パスパラメータitem_id
とクエリパラメータq
を受け取ります。item_id: int
は、item_id
が整数型であることを示しています。FastAPIはこの型ヒントを見て、URLパスから抽出した値を自動的に整数に変換しようとします。変換できない場合は、検証エラー(422)を返します。q: str | None = None
は、q
が文字列型またはNone
(None
はオプションであることを意味します)であることを示しています。デフォルト値がNone
なので、このクエリパラメータは必須ではありません。
- これらの関数も、Pythonの辞書を返しており、FastAPIがJSONに変換します。
3. アプリケーションの実行
作成したFastAPIアプリケーションをUvicornサーバーを使って実行します。ターミナルでmain.py
があるディレクトリに移動し、以下のコマンドを実行してください。
bash
uvicorn main:app --reload
uvicorn
: 実行するサーバー名です。main:app
:main
モジュール(main.py
ファイル)の中にあるapp
オブジェクト(FastAPIアプリケーションインスタンス)を指定しています。--reload
: このオプションを付けると、コードの変更を検知してサーバーが自動的に再起動します。開発中に非常に便利です。
サーバーが起動すると、以下のような出力が表示されます。
INFO: Will watch for changes in these directories: ['/path/to/your/project']
INFO: Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO: Started reloader process [#####]
INFO: Started server running on http://127.0.0.1:8000
これは、アプリケーションがhttp://127.0.0.1:8000
(ローカルホストのポート8000番)で待ち受け状態になったことを示しています。
4. APIへのアクセスとドキュメントの確認
ブラウザやcurl
などのツールを使って、APIにアクセスしてみましょう。
http://127.0.0.1:8000/
にアクセス:
json
{"Hello": "World"}
と表示されるはずです。http://127.0.0.1:8000/items/5
にアクセス:
json
{"item_id": 5, "q": null}
と表示されます。item_id
が整数として認識され、q
は指定されなかったのでnull
になっています。http://127.0.0.1:8000/items/5?q=somequery
にアクセス:
json
{"item_id": 5, "q": "somequery"}
と表示されます。クエリパラメータq
が認識されています。http://127.0.0.1:8000/items/fifty
のようにitem_id
に整数以外の値を指定すると、自動的に422エラーが返されます。
そして、FastAPIの素晴らしい機能である自動ドキュメントを確認してみましょう。
http://127.0.0.1:8000/docs
にアクセス:
Swagger UIによるインタラクティブなAPIドキュメントが表示されます。先ほど定義した/
と/items/{item_id}
のエンドポイントが表示され、それぞれの詳細(パラメータ、レスポンス形式など)が確認できます。各エンドポイントを展開すると、「Try it out」ボタンが表示され、ブラウザ上から実際にリクエストを送信してレスポンスを確認できます。http://127.0.0.1:8000/redoc
にアクセス:
ReDocによるドキュメントが表示されます。こちらはより可読性の高いドキュメント形式です。
コードに数行追加するだけで、このように機能するAPIと、そのAPI仕様を完璧に反映したインタラクティブなドキュメントが手に入りました。これがFastAPIの大きな強みの一つです。
ルーティングとパス操作
FastAPIでは、HTTPメソッドに対応するデコレーター(@app.get
, @app.post
, @app.put
, @app.delete
, @app.options
, @app.head
, @app.patch
, @app.trace
)を使って、特定のエンドポイントへのリクエストを処理する関数(パスオペレーション関数)を定義します。
パスパラメータ
URLの一部をパラメータとして受け取るには、パス文字列中に波括弧{}
でパラメータ名を指定します。
python
@app.get("/users/{user_id}")
async def read_user(user_id: int):
return {"user_id": user_id}
関数シグネチャで対応する名前の引数を定義し、型ヒントを付けます。FastAPIはパスから抽出した値をその型に自動変換しようとします。
クエリパラメータ
URLの?
以降に続くkey=value
形式のパラメータはクエリパラメータと呼ばれます。関数シグネチャでパスパラメータ以外の引数として定義すると、FastAPIはそれらをクエリパラメータとして扱います。
“`python
from fastapi import FastAPI
app = FastAPI()
@app.get(“/search/”)
async def search_items(query: str, limit: int = 10, offset: int = 0):
# query は必須の文字列クエリパラメータ
# limit はオプションの整数クエリパラメータ、デフォルト値は10
# offset はオプションの整数クエリパラメータ、デフォルト値は0
return {“query”: query, “limit”: limit, “offset”: offset}
“`
- 型ヒントにより、値は自動的に指定された型に変換されます。
- デフォルト値を指定することで、そのクエリパラメータはオプションになります。デフォルト値を指定しない引数は必須クエリパラメータとして扱われます。
パラメータの追加検証とメタデータ
クエリパラメータやパスパラメータに対して、単なる型変換だけでなく、追加の検証ルールを設けたり、OpenAPIドキュメント用のメタデータを追加したりしたい場合があります。その際には、Query
やPath
といったクラスを使用します。
“`python
from fastapi import FastAPI, Query, Path
app = FastAPI()
@app.get(“/items/{item_id}”)
async def read_item(
item_id: int = Path(…, title=”The ID of the item to get”, gt=0), # パスパラメータの検証とメタデータ
q: str | None = Query(None, alias=”item-query”, title=”Query string for the item”, min_length=3, max_length=50, regex=”^fixedquery$”), # クエリパラメータの検証とメタデータ
hidden_query: str | None = Query(None, include_in_schema=False) # ドキュメントに含めないクエリパラメータ
):
results = {“item_id”: item_id}
if q:
results.update({“q”: q})
return results
“`
Path(...)
やQuery(None, ...)
のように使用します。第一引数はデフォルト値です。...
は「必須」という意味になります。title
,description
はドキュメント用のメタデータです。gt
,lt
,ge
,le
(Greater Than, Less Than, Greater Equal, Less Equal) は数値の範囲検証です。min_length
,max_length
,regex
は文字列の検証です。alias
はパラメータ名を変更したい場合に使います(例: URLではitem-query
だが、関数内ではq
として使う)。include_in_schema=False
を指定すると、そのパラメータは自動生成されるドキュメントに含まれなくなります。
これらのPath
やQuery
クラスは、FastAPIが内部でPydanticのバリデーション機能を活用して実装されています。
リクエストボディの扱い (Pydanticモデル)
PUT, POST, PATCHリクエストなどで送られてくるリクエストボディ(通常はJSON形式)を扱うには、Pydanticモデルが非常に便利です。
Pydanticモデルの定義
まず、リクエストボディの構造を表すPydanticモデルを定義します。
“`python
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
# 例: サンプルデータの設定
class Config:
json_schema_extra = {
"example": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
}
“`
BaseModel
を継承したクラスを作成し、クラス変数としてフィールド名と型ヒントを定義します。オプションのフィールドにはデフォルト値を与えるか、| None = None
(またはOptional[type] = None
)とします。
Config
クラス内のjson_schema_extra
を使うと、自動生成されるOpenAPIドキュメントに例データを含めることができます。
リクエストボディとしての利用
定義したPydanticモデルを、パスオペレーション関数の引数として型ヒント付きで宣言します。
“`python
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.post(“/items/”)
async def create_item(item: Item):
# item は Pydantic モデルのインスタンスとして検証済みのデータを含む
item_dict = item.model_dump() # Pydantic v2 以降は model_dump(), v1 は dict()
if item.tax:
price_with_tax = item.price + item.tax
item_dict.update({“price_with_tax”: price_with_tax})
return item_dict
“`
FastAPIは、以下の処理を自動で行います。
- リクエストからJSONボディを読み込む。
- 読み込んだデータを、関数シグネチャで宣言された
Item
モデルを使って検証する。 - 検証が成功した場合、
Item
モデルのインスタンスを作成し、関数の引数item
として渡す。 - 検証が失敗した場合、詳細なエラーメッセージと共に422 Unprocessable Entityレスポンスを返す。
これにより、開発者はリクエストボディのパースや検証のコードを書くことなく、すぐに構造化されたデータとして扱うことができます。
ネストされたモデルとリスト
Pydanticモデルは、他のPydanticモデルをフィールドとして持つことができます。
“`python
from pydantic import BaseModel
from typing import List
class Address(BaseModel):
street: str
city: str
zip_code: str
class User(BaseModel):
name: str
age: int
addresses: List[Address] = [] # リスト形式のフィールド
“`
パスオペレーション関数では、これらのモデルも同様に型ヒントとして使用できます。
python
@app.post("/users/")
async def create_user(user: User):
# user は検証済みの User オブジェクト
return user
リクエストボディとして、ネストされたJSON構造やJSON配列を扱うことができます。
その他のリクエストボディ形式
JSONだけでなく、フォームデータやファイルアップロードもFastAPIはサポートしています。Form
やFile
クラスを使用します。
“`python
from fastapi import FastAPI, Form, File, UploadFile
from typing import Annotated
app = FastAPI()
@app.post(“/login/”)
async def login(username: str = Form(…), password: str = Form(…)):
# フォームデータとして username と password を受け取る
return {“username”: username, “password”: password}
@app.post(“/uploadfile/”)
async def create_upload_file(file: Annotated[UploadFile, File(…)]):
# ファイルアップロードを受け取る
# UploadFile はファイル名、ファイルサイズ、ファイルオブジェクトなどを含む
# Annotated は Python 3.9 以降でより明示的に型を記述する方法
return {“filename”: file.filename}
“`
これらのクラスも、内部でPydanticのような検証機能を提供します。
応答モデル (Response Model)
APIからの応答(レスポンス)データの形式も、Pydanticモデルを使って定義し、検証することができます。これは、APIから返されるデータの構造を保証し、自動生成されるドキュメントに正確なレスポンススキーマを含めるために役立ちます。
パスオペレーションデコレーターのresponse_model
引数に、応答データの型ヒント(通常はPydanticモデル)を指定します。
“`python
from fastapi import FastAPI
from pydantic import BaseModel
from typing import List
class ItemBase(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
class ItemCreate(ItemBase):
pass # 作成時には Base モデルと同じフィールドを使う例
class Item(ItemBase):
id: int # 応答時には ID が付与されると仮定
app = FastAPI()
DBのダミーデータとして使うリスト
fake_db = {}
@app.post(“/items/”, response_model=Item) # 応答モデルとして Item を指定
async def create_item(item: ItemCreate): # リクエストボディは ItemCreate
item_dict = item.model_dump()
# DBに保存する処理をシミュレート
new_id = len(fake_db) + 1
fake_db[new_id] = {“id”: new_id, **item_dict}
return fake_db[new_id] # 返り値は Item の構造に合致している必要がある
@app.get(“/items/{item_id}”, response_model=Item) # 応答モデルとして Item を指定
async def read_item(item_id: int):
if item_id not in fake_db:
raise HTTPException(status_code=404, detail=”Item not found”)
return fake_db[item_id] # 返り値は Item の構造に合致している必要がある
@app.get(“/items/”, response_model=List[Item]) # 応答モデルとして Item のリストを指定
async def read_items():
return list(fake_db.values()) # 返り値は Item のリストの構造に合致している必要がある
“`
response_model
を指定することのメリット:
- 応答データの整形とフィルタリング: パスオペレーション関数が返したPythonオブジェクト(Pydanticモデルのインスタンス、辞書など)は、指定された
response_model
の型に従って検証・シリアライズされます。これにより、パスオペレーション関数がPydanticモデルに含まれない余分なフィールド(例えば、DBから取得したユーザーオブジェクトのパスワードハッシュなど)を含んでいても、response_model
で定義されたフィールドのみがクライアントに返されます。これはセキュリティやデータカプセル化の観点から非常に重要です。 - 自動ドキュメント: 指定された
response_model
に基づいて、OpenAPIドキュメントに正確な応答スキーマが生成されます。クライアント開発者は、APIが返すデータの構造を事前に把握できます。 - データの保証: APIが常に期待される形式のデータを返すことを保証します。
特に、データベースモデルやORMオブジェクトを直接返さず、専用のPydanticモデルをresponse_model
として定義することが推奨されます。これにより、内部的なデータ構造と外部に公開するAPIのデータ構造を分離できます。
依存性注入 (Dependency Injection)
FastAPIの依存性注入システムは、コードの再利用性、構造化、テスト容易性を高めるための強力な仕組みです。共通の処理(認証、データベースセッション取得、設定値の読み込みなど)を「依存性」として定義し、パスオペレーション関数がその依存性を必要とすることを宣言することで、FastAPIが自動的に依存性を解決して関数に渡してくれます。
依存性の定義と利用
依存性は、引数を持つ(または持たない)任意の関数として定義できます。
“`python
from fastapi import FastAPI, Depends, Header, HTTPException
app = FastAPI()
依存性となる関数を定義
async def common_parameters(q: str | None = None, skip: int = 0, limit: int = 100):
# 共通のクエリパラメータを処理
return {“q”: q, “skip”: skip, “limit”: limit}
async def verify_token(x_token: str = Header(…)):
# Header から X-Token を受け取り、検証する依存性
if x_token != “fake-super-secret-token”:
raise HTTPException(status_code=400, detail=”X-Token header invalid”)
return x_token
async def verify_key(x_key: str = Header(…)):
# Header から X-Key を受け取り、検証する依存性
if x_key != “fake-super-secret-key”:
raise HTTPException(status_code=400, detail=”X-Key header invalid”)
return x_key
パスオペレーション関数で依存性を宣言
@app.get(“/items/”)
async def read_items(commons: dict = Depends(common_parameters), token: str = Depends(verify_token)):
# common_parameters 関数の戻り値が commons 引数に渡される
# verify_token 関数が実行され、その戻り値が token 引数に渡される(検証失敗時はここで HTTPException が発生)
return commons
@app.get(“/users/”, dependencies=[Depends(verify_token), Depends(verify_key)])
async def read_users():
# dependencies リストで複数の依存性を指定することもできる
# この場合、依存性の戻り値はパスオペレーション関数の引数として渡されないが、処理は実行される
return [{“username”: “Rick”}, {“username”: “Morty”}]
“`
Depends()
関数は、依存性を解決する関数を指定します。- パスオペレーション関数の引数として
引数名: 型ヒント = Depends(依存性関数)
の形式で宣言します。依存性関数の戻り値がこの引数に渡されます。 - 依存性関数自体も、他の依存性を
Depends()
を使って宣言することができます(サブ依存性)。FastAPIは依存性のチェーンを解決します。 - 依存性関数内で
HTTPException
を発生させると、その時点でリクエスト処理が中断され、エラーレスポンスがクライアントに返されます。これは認証失敗などのハンドリングに便利です。 - パスオペレーションデコレーターの
dependencies
引数に依存性のリストを指定することも可能です。この場合、依存性関数の戻り値はパスオペレーション関数に渡されませんが、前処理として依存性関数が実行されます。認証など、戻り値が不要で実行自体が目的の依存性に向いています。
yield を使った依存性 (Startup/Shutdown)
データベース接続やリソースの確保・解放など、パスオペレーションの開始前に行う初期化処理と、完了後に行うクリーンアップ処理が必要な場合があります。FastAPIの依存性注入システムは、ジェネレーター関数(yield
を使う関数)を依存性として使うことで、これを elegantly に扱えます。
“`python
from fastapi import FastAPI, Depends
from typing import Generator
import time
ダミーのデータベース接続を作成・解放する依存性
def get_db() -> Generator:
print(“Connecting to DB…”)
db = “fake database connection” # ダミーの接続オブジェクト
try:
yield db # ここで依存性の解決として db を提供
finally:
print(“Closing DB connection…”)
# 接続解放などのクリーンアップ処理
db = None # リソース解放のシミュレーション
app = FastAPI()
@app.get(“/items/”)
async def read_items(db_connection: str = Depends(get_db)):
# get_db() の yield db によって得られた “fake database connection” が db_connection に渡される
print(f”Using DB connection: {db_connection}”)
# DBを使った処理のシミュレーション
time.sleep(0.1)
return [{“id”: 1, “name”: “item1”}]
アプリケーションの起動時/終了時に一度だけ実行される依存性も定義可能
@app.on_event(“startup”)
async def startup_event():
# グローバルな DB コネクションプールを作成するなどの処理
pass
@app.on_event(“shutdown”)
async def shutdown_event():
# DB コネクションプールを閉じるなどの処理
pass
“`
yield
を含む関数をDepends()
に渡すと、リクエスト処理の開始時にyield
の前の部分が実行され、yield
から返された値が依存性として提供されます。そして、リクエスト処理が完了した後(応答が生成され、クライアントに送り返される直前など)に、yield
の後の部分(finally
ブロックを含む)が実行されます。これにより、リクエストごとのリソース管理(DBセッションの開始・コミット/ロールバック・クローズなど)を依存性として定義できます。
依存性のオーバーライド (テスト用)
テストを書く際に、特定の依存性の振る舞いをモックやスタブに差し替えたい場合があります。FastAPIはこれをapp.dependency_overrides
を使って簡単に実現できます。
“`python
main.py
from fastapi import FastAPI, Depends
async def get_settings():
return {“database_url”: “real_db_url”}
app = FastAPI()
@app.get(“/settings/”)
async def read_settings(settings: dict = Depends(get_settings)):
return settings
test_main.py
from fastapi.testclient import TestClient
from main import app, get_settings # テスト対象のアプリと依存性をインポート
テスト用の依存性関数
async def override_get_settings():
return {“database_url”: “mock_db_url”}
TestClient を作成
client = TestClient(app)
def test_read_settings():
# テストスイート全体、または特定のテスト関数に対して依存性をオーバーライド
app.dependency_overrides[get_settings] = override_get_settings
response = client.get("/settings/")
assert response.status_code == 200
assert response.json() == {"database_url": "mock_db_url"}
# テストが終わったらオーバーライドを元に戻す(通常はテストフレームワークがやってくれるが、明示的に行う場合)
# app.dependency_overrides = {}
“`
これは、データベース接続、外部サービス呼び出し、認証メカニズムなど、実際の実行には環境設定や外部リソースが必要な依存性を、テスト時は簡単に分離・制御できるため、単体テストや結合テストを書きやすくします。
FastAPIの依存性注入システムは、これらの機能により、大規模で複雑なアプリケーションにおいて、コードのモジュール性、テスト容易性、保守性を大幅に向上させます。
セキュリティ
API開発においてセキュリティは非常に重要です。FastAPIは、認証や認可のためのツールをfastapi.security
モジュールで提供しています。これらは依存性注入システムと組み合わせて使用するように設計されています。
代表的なセキュリティスキームをいくつか見てみましょう。
HTTP Basic Auth
基本的なユーザー名とパスワードによる認証です。
“`python
from fastapi import FastAPI, Depends
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from fastapi import HTTPException, status
app = FastAPI()
security = HTTPBasic()
def get_current_username(credentials: HTTPBasicCredentials = Depends(security)):
# ダミーの認証ロジック
if credentials.username == “admin” and credentials.password == “secret”:
return credentials.username
else:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=”Incorrect username or password”,
headers={“WWW-Authenticate”: “Basic”},
)
@app.get(“/users/me/”)
async def read_current_user(username: str = Depends(get_current_username)):
return {“username”: username}
“`
HTTPBasic()
のインスタンスを Depends()
で使用すると、FastAPIはリクエストのAuthorization
ヘッダーからユーザー名とパスワードを抽出し、HTTPBasicCredentials
オブジェクトとして依存性関数に渡します。依存性関数内で認証を行い、成功すればユーザー情報などを返し、失敗すればHTTPException
(通常は401 Unauthorized)を発生させます。
OAuth2 (Password Flow with Bearer)
最も一般的な認証スキームの一つであるOAuth2のパスワードフロー(古いですが、多くのAPIで使われています)とBearerトークンをサポートしています。これはJWTベースの認証を実装する際の出発点となります。
“`python
from fastapi import FastAPI, Depends
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from fastapi import HTTPException, status
app = FastAPI()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl=”token”) # トークンを取得するエンドポイントを指定
ダミーのユーザーデータベース
fake_users_db = {
“johndoe”: {
“username”: “johndoe”,
“full_name”: “John Doe”,
“email”: “[email protected]”,
“hashed_password”: “fakehashedsecret”, # 実際は bcrypt などでハッシュ化する
“disabled”: False,
}
}
def verify_password(plain_password: str, hashed_password: str) -> bool:
# 実際は bcrypt.checkpw などを使う
return plain_password + “hashed” == hashed_password
def get_user(db, username: str):
if username in db:
user_dict = db[username]
return user_dict
@app.post(“/token”)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
user = get_user(fake_users_db, form_data.username)
if not user or not verify_password(form_data.password, user[“hashed_password”]):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=”Incorrect username or password”,
headers={“WWW-Authenticate”: “Bearer”},
)
# ここで JWT を生成する
access_token = “fake-access-token-for-” + user[“username”]
return {“access_token”: access_token, “token_type”: “bearer”}
async def get_current_active_user(token: str = Depends(oauth2_scheme)):
# JWT をデコード・検証し、ユーザーを取得するロジック
# この例では簡易的にトークン文字列をチェック
if not token.startswith(“fake-access-token-for-“):
raise HTTPException(status_code=401, detail=”Invalid token”)
username = token.split(“fake-access-token-for-“)[1]
user = get_user(fake_users_db, username)
if user is None:
raise HTTPException(status_code=401, detail=”User not found”)
if user[“disabled”]:
raise HTTPException(status_code=400, detail=”Inactive user”)
return user
@app.get(“/users/me/”)
async def read_users_me(current_user: dict = Depends(get_current_active_user)):
return current_user
“`
OAuth2PasswordBearer(tokenUrl="token")
は、token
というパスでクライアントがユーザー名とパスワードを送信し、トークンを取得することを期待するセキュリティスキームを定義します。このスキームをDepends()
で利用すると、FastAPIはリクエストのAuthorization: Bearer <token>
ヘッダーからトークン文字列を抽出し、依存性関数に渡します。依存性関数内でトークンの検証(JWTの検証など)を行い、認証されたユーザー情報を返します。
FastAPI自身はJWTの生成や検証機能は提供しませんが、python-jose
, PyJWT
といったライブラリと組み合わせて簡単に実装できます。
その他のセキュリティ機能
fastapi.security.OAuth2AuthorizationCodeBearer
,OAuth2ImplicitBearer
など、OAuth2の他のフローもサポートしています。- スコープ(Scope)の定義と検証もサポートしており、ユーザーがAPIのどの部分にアクセスできるかを制御できます。
これらのセキュリティ機能は、FastAPIの自動ドキュメント (/docs
) にも反映され、どのエンドポイントにどの認証が必要かが表示されます。
データベース連携
FastAPIは特定のデータベースやORMに依存していません。同期・非同期の様々なデータベースライブラリやORMと組み合わせて使用できます。
- 同期ORM: SQLAlchemy (最も一般的), Django ORM (DjangoプロジェクトをFastAPIと組み合わせる場合など)
- 非同期ORM: SQLModel (FastAPI作者が開発, Pydantic+SQLAlchemy), SQLAlchemy 2.0 (asyncio対応), Tortoise ORM, Ormar
- 非同期DBドライバー: asyncpg (PostgreSQL), aiosqlite (SQLite), motor (MongoDB)
非同期対応のライブラリを使用する場合、パスオペレーション関数をasync def
で定義し、DBアクセスをawait
する必要があります。同期ライブラリを使用する場合でも、FastAPIは内部的にスレッドプールを使ってブロッキング処理を非同期的に実行しますが、パフォーマンスが重要な場合は非同期ライブラリが推奨されます。
依存性注入システムは、リクエストごとにDBセッションを取得・解放するのに非常に便利です。
“`python
from fastapi import FastAPI, Depends, HTTPException, status
from sqlalchemy import create_engine, Column, Integer, String
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base # 同期版の例
— DB 設定 (同期版 SQLAlchemy) —
SQLALCHEMY_DATABASE_URL = “sqlite:///./sql_app.db” # SQLite の例
connect_args={“check_same_thread”: False} は SQLite のみ必要
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={“check_same_thread”: False})
各リクエストで使用するセッションを作成するクラス
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
ORM モデルのベース
Base = declarative_base()
DB テーブルの定義
class Item(Base):
tablename = “items”
id = Column(Integer, primary_key=True, index=True)
title = Column(String, index=True)
description = Column(String, index=True)
テーブル作成 (初回実行時など)
Base.metadata.create_all(bind=engine)
— 依存性: DB セッションの取得とクローズ —
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close() # リクエスト完了後にセッションをクローズ
— Pydantic モデル (リクエスト/レスポンス用) —
from pydantic import BaseModel
class ItemCreate(BaseModel):
title: str
description: str | None = None
class Item(BaseModel):
id: int
title: str
description: str | None = None
class Config:
orm_mode = True # SQLAlchemy オブジェクトを Pydantic モデルにマッピング可能にする (v1 の場合)
# v2 の場合は from_attributes = True に変更
— FastAPI アプリケーション —
app = FastAPI()
@app.post(“/items/”, response_model=Item)
def create_item(item: ItemCreate, db: SessionLocal = Depends(get_db)):
# db は get_db 依存性から提供された SQLAlchemy セッション
db_item = Item(title=item.title, description=item.description)
db.add(db_item)
db.commit() # DB に保存
db.refresh(db_item) # DB から最新の状態を読み込む
return db_item # SQLAlchemy オブジェクトだが、response_model により Pydantic モデルに変換される
@app.get(“/items/”, response_model=list[Item])
def read_items(skip: int = 0, limit: int = 100, db: SessionLocal = Depends(get_db)):
items = db.query(Item).offset(skip).limit(limit).all()
return items
@app.get(“/items/{item_id}”, response_model=Item)
def read_item(item_id: int, db: SessionLocal = Depends(get_db)):
item = db.query(Item).filter(Item.id == item_id).first()
if item is None:
raise HTTPException(status_code=404, detail=”Item not found”)
return item
“`
この例では同期版のSQLAlchemyを使用していますが、get_db
依存性関数をasync def
で定義し、非同期対応のSQLAlchemyやORMを使用すれば、非同期DBアクセスも同様に実現できます。
get_db
のような依存性関数は、リクエスト処理の開始時にDBセッションを作成し、yield
でセッションをパスオペレーション関数に提供します。パスオペレーション関数が完了すると(正常終了または例外発生)、finally
ブロックが実行され、セッションが適切にクローズされます。これにより、リクエストごとのDBセッション管理がシンプルかつ堅牢に行えます。
SQLModel
FastAPIの作者であるTiangolo氏が開発しているSQLModelは、FastAPI、Pydantic、SQLAlchemyを組み合わせたライブラリです。Pydanticモデルを使ってデータベーステーブルとAPIスキーマを同時に定義でき、同期・非同期の両方のDB操作に対応しています。FastAPIでデータベースを扱う場合、SQLModelは非常に強力な選択肢となります。
ミドルウェア
ミドルウェアは、FastAPIアプリケーションが実際のリクエストハンドリングを行う前後に処理を挿入するための仕組みです。ログ記録、認証、CORS(Cross-Origin Resource Sharing)、GZip圧縮、レート制限など、様々な用途に利用できます。
FastAPI(基盤であるStarlette)は、いくつかの便利なミドルウェアを提供しており、app.add_middleware()
メソッドを使って追加できます。
“`python
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.gzip import GZipMiddleware
app = FastAPI()
CORS ミドルウェアの追加
許可するオリジン、メソッド、ヘッダーなどを設定
app.add_middleware(
CORSMiddleware,
allow_origins=[““], # 実際は許可するオリジンリストを指定すべき
allow_credentials=True,
allow_methods=[““],
allow_headers=[“*”],
)
GZip 圧縮ミドルウェアの追加
レスポンスを自動的に GZip 圧縮する (Accept-Encoding が gzip の場合)
app.add_middleware(GZipMiddleware, minimum_size=1000) # 1000 バイト以上のレスポンスに適用
@app.get(“/”)
async def read_root():
return {“Hello”: “World”}
“`
add_middleware()
は、追加するミドルウェアクラスと、そのミドルウェアクラスの初期化に必要な引数を受け取ります。ミドルウェアはadd_middleware
で追加された順序でリクエスト処理チェーンに追加されます。
カスタムミドルウェアの作成
独自のミドルウェアを作成することも可能です。ASGIミドルウェアは、以下のシグネチャを持つ非同期関数として定義されます。
async def middleware(scope, receive, send)
より簡単に、クラスベースでカスタムミドルウェアを作成することも推奨されています。StarletteのBaseHTTPMiddleware
を使うと、通常のRequest/Responseオブジェクトを使って処理を記述できます。
“`python
from fastapi import FastAPI, Request
from starlette.middleware.base import BaseHTTPMiddleware
class CustomHeaderMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request: Request, call_next):
# リクエスト処理の前に実行されるコード
start_time = time.time()
response = await call_next(request) # 次のミドルウェアまたはエンドポイントを呼び出す
# リクエスト処理の後に実行されるコード
process_time = time.time() – start_time
response.headers[“X-Process-Time”] = str(process_time) # ヘッダーを追加
return response
app = FastAPI()
app.add_middleware(CustomHeaderMiddleware)
@app.get(“/”)
async def read_root():
return {“Hello”: “World”}
“`
BaseHTTPMiddleware
を継承したクラスのdispatch
メソッドを実装します。dispatch
メソッドはrequest
とcall_next
を受け取ります。call_next(request)
を呼び出すことで、次のミドルウェアや最終的なパスオペレーション関数にリクエストが渡されます。その結果(Responseオブジェクト)を受け取り、必要に応じて変更を加えて返します。
カスタムミドルウェアは、リクエストの前処理(認証、ロギング、レート制限など)や、レスポンスの後処理(ヘッダー追加、データ変換など)に非常に柔軟に対応できます。
エラーハンドリング
API開発では、エラー発生時にクライアントに適切なステータスコードとエラーメッセージを返すことが重要です。FastAPIは標準的なHTTPエラーハンドリング機構を提供します。
HTTPException
FastAPIで最も一般的なエラーハンドリング方法は、fastapi.HTTPException
を発生させることです。
“`python
from fastapi import FastAPI, HTTPException, status
app = FastAPI()
items = {“foo”: “The Foo Wrestlers”}
@app.get(“/items/{item_id}”)
async def read_item(item_id: str):
if item_id not in items:
# 404 Not Found エラーを発生させる
raise HTTPException(status_code=404, detail=”Item not found”)
return {“item”: items[item_id]}
@app.post(“/items/”)
async def create_item(name: str):
if name in items:
# 409 Conflict エラーを発生させる例
raise HTTPException(
status_code=status.HTTP_409_CONFLICT, # status モジュールから HTTP ステータスコードを取得すると便利
detail=f”Item with name ‘{name}’ already exists”
)
items[name] = f”Item {name} created”
return {“status”: “created”, “name”: name}
“`
HTTPException
のコンストラクタには、status_code
(HTTPステータスコード)とdetail
(エラーの詳細メッセージ、クライアントに返されるJSONのdetail
フィールドの値になる)を指定します。追加でheaders
引数を使ってレスポンスヘッダーを追加することもできます(例: 認証失敗時のWWW-Authenticate
ヘッダー)。
HTTPException
を発生させると、FastAPIはそれを捕捉し、指定されたステータスコードと詳細メッセージを持つJSONレスポンスを自動的に生成してクライアントに返します。
Pydantic バリデーションエラー
Pydanticによるリクエストデータの検証に失敗した場合、FastAPIは自動的に422 Unprocessable Entityステータスコードと、検証エラーの詳細を含むJSONレスポンスを返します。このデフォルトのレスポンスはOpenAPI仕様に基づいています。
例えば、前述のcreate_item
エンドポイント(Itemモデルを使用)に無効なデータを送信した場合:
json
{
"detail": [
{
"loc": [
"body",
"price"
],
"msg": "field required",
"type": "value_error.missing"
}
]
}
このような形式のエラーレスポンスが自動で生成されます。これはクライアントが検証エラーの原因を特定するのに役立ちます。
カスタム例外ハンドラー
特定の種類の例外(HTTPException
以外のカスタム例外や、Python標準の例外)を捕捉し、カスタムのレスポンスを返したい場合は、@app.exception_handler()
デコレーターを使用します。
“`python
from fastapi import FastAPI, Request, Response, status
from fastapi.responses import JSONResponse
class CustomException(Exception):
def init(self, name: str):
self.name = name
app = FastAPI()
@app.exception_handler(CustomException)
async def custom_exception_handler(request: Request, exc: CustomException):
# CustomException が発生した場合に実行されるハンドラー
return JSONResponse(
status_code=status.HTTP_418_IM_A_TEAPOT, # 例として 418 を使う
content={“message”: f”Oops! CustomException ‘{exc.name}’ happened.”},
)
@app.get(“/custom-exception/”)
async def trigger_custom_exception():
raise CustomException(name=”MyNamedException”)
“`
@app.exception_handler()
の引数に、捕捉したい例外の型を指定します。ハンドラー関数はリクエストオブジェクトと発生した例外オブジェクトを受け取り、Response
オブジェクト(JSONResponse
など)を返す必要があります。
これにより、アプリケーション固有の複雑なエラーケースや、予期せぬエラー(未処理の例外)に対して、統一的で分かりやすいエラーレスポンスを提供できます。
テスト
FastAPIアプリケーションは、依存性注入システムとテストクライアントのおかげで非常にテストしやすい構造になっています。
TestClient
fastapi.testclient.TestClient
クラスを使うと、実際のHTTPリクエストをシミュレートしてFastAPIアプリケーションをテストできます。Webサーバーを起動する必要はありません。
“`python
test_main.py
from fastapi.testclient import TestClient
from main import app # テスト対象の FastAP 呼びケーションをインポート
TestClient を作成
client = TestClient(app)
テスト関数 (pytest と連携)
def test_read_main():
response = client.get(“/”) # GET リクエストを送信
assert response.status_code == 200 # ステータスコードを検証
assert response.json() == {“Hello”: “World”} # レスポンスボディを検証
def test_read_item():
response = client.get(“/items/foo?q=baz”)
assert response.status_code == 200
assert response.json() == {“item_id”: “foo”, “q”: “baz”}
def test_read_item_bad_item_id():
response = client.get(“/items/baz”) # 存在しないアイテム
assert response.status_code == 404 # 404 が返るはず
assert response.json() == {“detail”: “Item not found”} # エラーメッセージも検証
“`
TestClient
のメソッド(get
, post
, put
, delete
など)は、requests
ライブラリと似たインターフェースを提供します。response
オブジェクトもrequests
のResponseオブジェクトと似ており、status_code
, json()
, text
などの属性やメソッドを持ちます。
pytest
のようなテストフレームワークと一緒に使うと、テストの実行、レポート生成、フィクスチャ管理などが容易になります。
依存性のオーバーライド(テスト時)
前述の依存性注入のセクションで説明したように、テスト時に特定の依存性をモックやスタブに差し替えることができます。これは、データベース、外部API、認証メカニズムなど、テストが困難な外部依存関係を持つコードをテストする際に非常に重要です。
“`python
main.py (再掲)
from fastapi import FastAPI, Depends
async def get_settings():
# 実際の DB 接続情報などを返す関数
return {“database_url”: “real_db_url”}
app = FastAPI()
@app.get(“/settings/”)
async def read_settings(settings: dict = Depends(get_settings)):
return settings
test_main.py (再掲)
from fastapi.testclient import TestClient
from main import app, get_settings
テスト用の依存性オーバーライド関数
async def override_get_settings():
return {“database_url”: “mock_db_url”}
client = TestClient(app)
def test_read_settings_with_override():
# テスト対象の依存性を一時的に差し替える
app.dependency_overrides[get_settings] = override_get_settings
response = client.get("/settings/")
assert response.status_code == 200
assert response.json() == {"database_url": "mock_db_url"}
# オーバーライドを解除する(通常はテストフィクスチャや conftest.py で管理)
# この例では明示的に削除
del app.dependency_overrides[get_settings]
他のテスト関数では get_settings はオーバーライドされていない
def test_read_settings_without_override():
response = client.get(“/settings/”)
assert response.status_code == 200
# オーバーライドされていないので、本来の get_settings の戻り値が返る
assert response.json() == {“database_url”: “real_db_url”}
“`
TestClient
を使用し、テスト関数内でapp.dependency_overrides
辞書を変更することで、そのテスト実行中のみ特定の依存性の振る舞いを変更できます。テスト後には元に戻すか、テストフレームワークのフィクスチャを使って管理するのが一般的です。
この強力な機能により、FastAPIアプリケーションの各コンポーネントを分離して、より信頼性の高い単体テストや統合テストを作成できます。
デプロイメント
FastAPIアプリケーションを本番環境で実行するには、UvicornやHypercornのようなASGIサーバーが必要です。開発時にはuvicorn main:app --reload
のようなコマンドで実行しますが、本番環境では--reload
オプションは使用せず、ワーカープロセスを複数起動してスケーラビリティと信頼性を向上させます。
Uvicorn の本番向け実行
Gunicornなどのプロセス管理ツールを使ってUvicornワーカーを管理するのが一般的な方法です。
“`bash
pip install gunicorn uvicorn[standard]
4つのワーカープロセスで実行する例
gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app
“`
-w 4
: ワーカープロセス数を4つに設定します。CPUコア数やI/Oバウンドの度合いに応じて調整します。-k uvicorn.workers.UvicornWorker
: Uvicornワーカークラスを指定します。main:app
: 実行するFastAPIアプリケーションを指定します(main.py
ファイルのapp
インスタンス)。
コンテナ環境(Docker)でのデプロイも非常に一般的です。公式ドキュメントでは、プロダクション向けに最適化されたDockerイメージ(tiangolo/uvicorn-gunicorn-fastapi)が提供されています。
Docker を使ったデプロイ
Dockerfile
を作成し、アプリケーションと依存関係をコンテナイメージにパッケージングします。
“`dockerfile
Dockerfile
FROM python:3.10-slim
環境変数を設定 (必要に応じて)
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
アプリケーションコードをコンテナにコピー
WORKDIR /app
COPY ./requirements.txt /app/
RUN pip install –no-cache-dir -r requirements.txt
COPY ./app /app/app
公式イメージを使用する場合は、こちらを使うのが簡単で最適化されています
FROM tiangolo/uvicorn-gunicorn-fastapi:python3.10
COPY ./app /app
アプリケーションを実行
CMD [“uvicorn”, “app.main:app”, “–host”, “0.0.0.0”, “–port”, “80”]
gunicorn を使う場合 (tiangolo イメージはデフォルトでこれを使います)
CMD [“gunicorn”, “-w”, “4”, “-k”, “uvicorn.workers.UvicornWorker”, “app.main:app”, “–bind”, “0.0.0.0:80”]
“`
このDockerfileを使ってイメージをビルドし、実行します。
bash
docker build -t my-fastapi-app .
docker run -d --name fastapi-instance -p 80:80 my-fastapi-app
これにより、FastAPIアプリケーションがDockerコンテナ内で起動し、ホストの80番ポートに公開されます。
クラウドプラットフォーム
Dockerコンテナとしてパッケージングすれば、AWS ECS/EKS, Google Cloud Run/GKE, Azure Container Instances/AKS, Heroku, Vercel, Renderなど、様々なクラウドプラットフォームに容易にデプロイできます。これらのサービスはコンテナの管理、スケーリング、負荷分散などを担当してくれます。
サーバーレス機能(AWS Lambda, Google Cloud Functionsなど)でFastAPIを動かすためのアダプターライブラリ(例: mangum)も存在しますが、ASGIの非同期特性を活かすには、常時起動のコンテナ環境やVM環境の方が一般的には適しています。
FastAPIのエコシステム
FastAPIの人気上昇に伴い、FastAPIと連携する多くの便利なライブラリやツールが登場しています。
- SQLModel: PydanticとSQLAlchemyを統合し、型ヒントを使ってDBモデルとPydanticモデルを一度に定義できるORMライブラリ。FastAPIとの親和性が非常に高いです。
- FastAPI Users: ユーザー認証(登録、ログイン、パスワードリセットなど)機能を提供するライブラリ。様々なバックエンド(SQLAlchemy, Tortoise ORM, MongoDBなど)に対応しています。
- FastAPI Admin: FastAPIアプリケーションのための管理画面を自動生成するライブラリ。DBモデルに基づいてCRUDインターフェースを提供します。
- FastAPI Cache: APIレスポンスのキャッシュ機能を提供するライブラリ。Redisなどをバックエンドに使用できます。
- FastAPI Limiter: レート制限機能を提供するライブラリ。IPアドレスやユーザーIDに基づいてAPIコールの回数を制限できます。
- Mangum: ASGIアプリケーションをAWS Lambdaなどのサーバーレス環境で実行可能にするアダプター。
これらのライブラリを活用することで、ユーザー管理、管理画面、キャッシングといった一般的なAPI機能を素早く実装できます。
他のPythonフレームワークとの比較
FastAPIはPython Webフレームワークの風景に新たな選択肢をもたらしました。主要なフレームワークと比較してみましょう。
Flask
- FastAPI: モダン、非同期ネイティブ、型ヒント活用、自動ドキュメント、Pydantic統合、依存性注入。高性能、開発速度が速い。
- Flask: マイクロフレームワーク、シンプル、柔軟性が高い、同期メイン(Asyncio対応は後付け)。データ検証、ドキュメント、DB連携などは別途ライブラリを選択・統合する必要がある。小規模プロジェクトや学習に適しているが、API開発には追加の手間が必要。
FastAPIは、API開発に必要な機能を最初から統合し、型ヒントとPydanticで開発体験を大幅に向上させています。Flaskはより自由度が高いですが、その分開発者が多くの選択と実装を行う必要があります。API開発においては、多くの場合FastAPIの方が効率的でしょう。
Django
- FastAPI: API開発に特化、マイクロサービス向き、高いパフォーマンス、非同期ネイティブ、自動ドキュメント。
- Django: フルスタック、Webサイト開発全般向き、豊富なビルトイン機能(ORM, Admin, Forms, Authなど)、同期メイン(Asyncio対応は進行中)。大規模なWebアプリケーション全体を構築するのに強力だが、API開発に特化する場合は機能が多すぎることも。DRF(Django REST framework)と組み合わせるのが一般的だが、DRF + Djangoのスタックは非同期対応やパフォーマンス面でFastAPIに及ばないことがある。
FastAPIはAPIサーバーとしてシンプルかつ高性能を追求しており、マイクロサービスのバックエンドや、SPA/モバイルアプリのバックエンドとして適しています。DjangoはWebサイトとAPIが密接に関連しているようなモノリシックなアプリケーションや、Web管理画面が重要な場合に強力です。プロジェクトの性質と必要な機能に応じて選択が変わります。
Starlette
- FastAPI: Starletteの上に構築されている、API開発に特化した機能(Pydantic統合、依存性注入、自動ドキュメント)を追加。
- Starlette: 軽量なASGIフレームワーク、HTTP/WebSocket対応、ミドルウェア、起動/終了イベント。FastAPIよりも低レベルで、より高い自由度でアプリケーションを構築したい場合に適しています。例えば、GraphQLサーバー、WebSocketサーバーなど、HTTP REST API以外のものを構築する際にはStarlette単体の方が適している場合があります。
FastAPIはStarletteの機能を活用しつつ、API開発でよくある要件を解決するための高レベルな機能を提供しています。ほとんどのHTTP REST API開発においては、FastAPIを使うのが効率的でしょう。Starletteはよりニッチな用途や、FastAPIの機能セットに完全に合わないケースで検討する価値があります。
どのフレームワークを選ぶか?
- 高性能でモダンなAPIを素早く開発したい: FastAPI
- 小規模でシンプルなWebアプリ/API、最大限の柔軟性が欲しい: Flask
- 管理画面など付属機能も多く含むフルスタックWebアプリを開発したい: Django
- FastAPIの機能セットでは不十分な低レベルなASGIアプリケーションを構築したい: Starlette
もちろん、これらのフレームワークは組み合わせて使うことも可能です(例: DjangoでWebサイトを、FastAPIで高性能なAPI部分を構築するなど)。
まとめと今後の展望
FastAPIは、PythonにおけるWeb API開発の新たなスタンダードとなりうる強力なフレームワークです。その「速さ」(実行パフォーマンスと開発速度)は、現代のソフトウェア開発の要求に合致しています。
FastAPIの主な利点を改めてまとめると:
- 高いパフォーマンス: 非同期処理とASGIサーバーによる効率的なリクエスト処理。
- 驚異的な開発速度: 型ヒント、Pydantic、自動ドキュメント、依存性注入によるコード記述量の削減と開発効率向上。
- 堅牢性: Pydanticによる自動データ検証と型ヒントによる静的解析の恩恵。
- 自動ドキュメント: Swagger UI / ReDocによるインタラクティブで常に最新のAPI仕様提供。
- 標準技術の活用: Python型ヒント、OpenAPI、JSON Schemaに基づく。
- 優れたテスト容易性: 依存性注入システムとTestClient。
- 活発なコミュニティ: 作者やコントリビューターによる迅速な開発と手厚いサポート、豊富なドキュメント。
FastAPIは、特に以下のようなプロジェクトに適しています。
- マイクロサービスアーキテクチャにおけるバックエンドAPI
- SPA (Single Page Application) やモバイルアプリケーションのバックエンドAPI
- 高性能なデータ処理API
- 内部向け・外部向け問わず、仕様が明確で自己文書化されるAPIが必要な場合
- 非同期I/O処理(DBアクセス、外部APIコールなど)が多いAPI
一方で、フルスタックの機能(テンプレートエンジン、管理画面、フォーム処理など)をフレームワーク自体に求める場合は、Djangoのようなフレームワークの方が適しているかもしれません。しかし、APIに特化し、フロントエンドや管理画面は別の技術スタックで構築する現代的なアプローチにおいては、FastAPIは非常に魅力的な選択肢です。
FastAPIは比較的新しいフレームワークですが、その設計思想、パフォーマンス、開発効率の高さから、Pythonコミュニティで急速に支持を広げています。エコシステムも成長しており、関連ライブラリも充実してきています。今後もPythonによるWeb API開発において、FastAPIの存在感はますます高まっていくと予想されます。
ぜひこの記事を参考に、FastAPIの世界に飛び込み、その強力さを体験してみてください。
参考文献/関連リンク
- FastAPI 公式ドキュメント: https://fastapi.tiangolo.com/ (非常に充実しており、日本語訳も提供されています)
- Starlette 公式ドキュメント: https://www.starlette.io/
- Pydantic 公式ドキュメント: https://docs.pydantic.dev/
- Uvicorn 公式ドキュメント: https://www.uvicorn.org/
- SQLModel 公式ドキュメント: https://sqlmodel.tiangolo.com/