FastAPIでWeb APIを開発するとき、PostgreSQLは有力なデータベース候補です。
ただし、FastAPIと組み合わせれば常に最速になるわけでも、すべてのシステムで最適になるわけでもありません。
複数ユーザーが同時にデータを更新する、関連する更新を一まとまりで扱う、JSONデータを条件検索する、非同期のデータベースアクセスを採用するといった要件があるなら、PostgreSQLの機能を生かしやすくなります。
この記事では、PostgreSQLの特徴をFastAPIの実装と結び付け、MySQLやSQLiteも含めた選定基準を整理します。
PostgreSQLが担う役割
PostgreSQLは、表同士の関係を定義してデータを管理するオープンソースのリレーショナルデータベース管理システム(RDBMS)です。
ユーザーと注文、商品と在庫のように、複数のデータを関連付けて扱うAPIに向いています。
FastAPIがリクエストの受付や入力検証を担当するのに対し、PostgreSQLはデータの保存、検索、整合性の維持を担当します。
- トランザクション
- 関連する複数の更新を一まとまりとして扱い、途中で失敗した場合は更新前の状態へ戻せる仕組みです。
- JSONB
- JSONを検索や索引付けに適した形式で保存するデータ型です。
- 行レベルセキュリティ
- 利用者や役割に応じて、参照または更新できる行を制限する機能です。
- 拡張機能
- 空間データを扱うPostGISなど、必要な機能を追加する仕組みです。
これらは個別の要件に応じて使う機能であり、採用するだけで性能や安全性が保証されるわけではありません。
FastAPIと非同期データベースアクセス
FastAPIは、async defとawaitを使った非同期処理に対応しています。
非同期処理では、アプリケーションがデータベースの応答を待つ間に、同じプロセスが別のリクエストを進められます。
これは待ち時間を使いやすくする仕組みであり、遅いSQLを速くしたり、データベースの処理能力を増やしたりする仕組みではありません。
FastAPIそのものの特徴は、関連記事「FastAPIとは?PythonでAPIを作るときの設計ポイント」で確認できます。
asyncpgを直接使う場合
asyncpgは、PythonからPostgreSQLへ非同期で接続するためのドライバです。
SQLを直接管理したい構成では、接続、トランザクション、クエリ実行を細かく制御できます。
pip install asyncpg
その代わり、接続の返却、例外時のロールバック、SQLとPythonオブジェクトの対応付けをアプリケーション側で設計する必要があります。
SQLAlchemyやSQLModelを使う場合
モデル定義、クエリ構築、トランザクション境界を共通化したい場合は、SQLAlchemyやSQLModelを候補にできます。
SQLAlchemyの非同期エンジンからasyncpgを使う接続URLは、次の形式です。
DATABASE_URL=postgresql+asyncpg://app_user:password@db-host/app_db
ORMを採用しても、SQLの実行回数、索引、トランザクションの範囲を確認する作業は残ります。
同期ライブラリを使う処理まで形式だけasync defに変えると、イベントループを待たせる可能性があるため、利用するドライバに合わせて同期と非同期を選びます。
JSONBを使う場面
JSONBは、項目が増減しやすい補助情報を保存し、その内容を検索したい場面で役立ちます。
たとえば、ユーザーIDやメールアドレスは通常の列で管理し、通知設定や画面表示の好みをJSONBにまとめる設計が考えられます。
FastAPI側ではPydanticモデルで入力形式を検証し、PostgreSQL側では保存後の検索条件や索引を設計します。
ただし、JSONBは表設計を不要にする機能ではありません。
必須項目、重複を許さない値、他の表と関連付ける値は通常の列として定義した方が、制約の意図を読み取りやすくなります。
PostgreSQL、MySQL、SQLiteの選び分け
三つのデータベースは、単純な点数ではなく、配置方法、同時書き込み、既存の運用資産、必要な機能で比較します。
| 候補 | 向きやすい状況 | 選定前の確認事項 |
|---|---|---|
| PostgreSQL | 複数ユーザーによる更新、複雑な検索、JSONB、行単位のアクセス制御、拡張機能が要件に含まれる | 運用環境、接続数、移行手順、採用するドライバを決める |
| MySQL | 既存システムやチームの標準がMySQLで、必要な機能をその構成で満たせる | 既存のSQL、データ型、運用ツールとの整合を確認する |
| SQLite | ローカル開発、小規模なツール、単一ファイルで管理したい用途 | 同時書き込みが増える見込みと、クライアントサーバー型データベースへ移行する条件を決める |
SQLiteは試作専用ではなく、要件が合えば実運用にも使えます。
一方で、同時に書き込める処理には制約があるため、更新が集中するAPIでは早い段階で負荷の性質を確認します。
MySQLも本番運用の選択肢であり、PostgreSQLより一律に劣るわけではありません。
既存のデータや運用手順を移す必要がある場合は、「MySQLからPostgreSQLへ移行する実務手順」も判断材料になります。
PostgreSQLを選ぶことで得やすい利点
関連する更新を一貫して扱える
注文の登録と在庫の更新など、片方だけ成功すると困る処理は、トランザクションの範囲を決めて実装できます。
APIの一回のリクエストとトランザクションをどこまで対応させるかは、業務上の整合性を基準に決めます。
表形式とJSONを一つのデータベースで扱える
関係が明確なデータは列と外部キーで管理し、形が変わりやすい補助情報はJSONBで扱えます。
JSONB内の値を検索する場合は、実際の検索条件に合わせた索引も検討します。
要件が増えたときに機能を追加できる
位置情報を扱うPostGISや文字列検索を補助するpg_trgmなど、用途に応じた拡張機能を選べます。
全文検索をAPIへ組み込む例は、「FastAPIで実践する全文検索APIの設計」で扱っています。
拡張機能は導入、更新、バックアップ、復元の手順にも関係するため、使うものだけを採用します。
データに近い場所でアクセス範囲を制御できる
行レベルセキュリティを使うと、役割や利用者に応じて参照または更新できる行を絞れます。
ただし、これはFastAPI側の認証や認可を置き換える機能ではありません。
アプリケーションとデータベースの両方で、想定した権限になるかをテストします。
導入時に決めておくこと
ライブラリの役割をそろえる
asyncpgを直接使うのか、SQLAlchemyやSQLModelを通して使うのかを先に決めます。
複数の方法を混在させると、接続とトランザクションを誰が管理するのか分かりにくくなるためです。
スキーマ変更を履歴として管理する
マイグレーションは、テーブルや列の変更を順番に適用できる形で保存する仕組みです。
SQLAlchemyやSQLModelを使う構成では、Alembicを候補にできます。
pip install alembic
alembic init alembic
生成された変更内容はそのまま適用せず、データの欠損や長時間のロックにつながらないかをレビューします。
接続情報をコードから分離する
データベースのホスト名、ユーザー名、パスワードは環境変数などから読み込み、ソースコードへ直接書かない構成にします。
.envを使う場合は、公開リポジトリへ含めない運用ルールも必要です。
接続プールの上限を決める
接続プールは、再利用できるデータベース接続をあらかじめ管理する仕組みです。
接続が少なすぎるとAPI側で待ち時間が増え、多すぎるとデータベースへ過剰な負荷をかけます。
アプリケーションのプロセス数、各プロセスのプール上限、データベースが許容する接続数を一つの計画として扱います。
FastAPIの起動時と終了時に共有リソースを管理する方法は、「FastAPIのlifespanで接続プールを管理する方法」で解説しています。
採用前の確認リスト
- 同時に読み書きする利用者や処理がどの程度あるか。
- 複数の更新を一つのトランザクションにする必要があるか。
- JSONB内の値を検索する必要があるか。
- PostGIS、pg_trgm、行レベルセキュリティなどの機能が要件に含まれるか。
- 同期ドライバと非同期ドライバのどちらを使うか。
- 接続プールをどの単位で作成し、いつ解放するか。
- スキーマ変更を誰がレビューし、どの環境から適用するか。
- 既存のMySQLやSQLiteから移行する費用が、得られる利点に見合うか。
選定の結論
FastAPIで、同時アクセスを受ける業務API、トランザクションを重視する処理、JSONBや拡張機能を使うシステムを構築するなら、PostgreSQLは検討価値の高い選択肢です。
その利点を生かすには、非同期ドライバを入れるだけでなく、SQL、索引、トランザクション、接続プール、マイグレーションを一つの運用設計として整える必要があります。
小規模なローカル用途ならSQLite、既存の運用基盤があるならMySQLが合理的な場合もあります。
データベース名から決めるのではなく、同時書き込み、整合性、検索方法、運用体制を先に言葉にすると、FastAPIに合う構成を選びやすくなります。

