FastAPIの「依存性注入(DI)」って何? なぜ必要なのかをコードで理解してみる

FastAPIのドキュメントを読んでいると、必ず出てくる**「依存性注入 (Dependency Injection: DI)」**という言葉。漢字が多くて難しそうに見えますが、実は「コードをきれいに、楽に書くための便利な仕組み」なんです。

この記事では、比喩を使わず、実際のコードの「困った例」と「解決した例」を見比べることで、FastAPIの依存性注入が何をしているのかを解説します。

1. 「依存」している状態とは?(困った例)

プログラミングにおいて、ある関数がWeb APIやデータベースなどを利用するとき、「その関数はAPIやデータベースに依存している」と言います。

例えば、「データベースからユーザー情報を取得する関数」を考えてみましょう。

python
# 【悪い例】関数の中で必要なツールを自分で用意している

# データベースに接続するためのライブラリ(架空)
import my_database_lib

def get_user_info(user_id: int):
    # 1. 関数の中で、データベースへの接続を自分で確立する
    # ※ ここで具体的な接続先URLなどを知っている必要がある!
    connection = my_database_lib.connect("db://localhost:5432", user="admin", password="password")

    print("DBに接続しました")

    try:
        # 2. 接続ツールを使ってデータを取得する
        user_data = connection.fetch_user(user_id)
        return user_data
    finally:
        # 3. 終わったら自分で接続を切る(忘れると大変!)
        connection.close()
        print("DB接続を切断しました")

# 使うとき
# print(get_user_info(1))

このコードの問題点

一見正しく動きますが、この関数は**「データベースへの接続作業」に強く依存**してしまっています。

  1. テストが非常に難しい: この関数をテストしようとすると、毎回本物のデータベースに接続しようとします。「テストの時だけ偽物のデータベース(モック)を使いたい」と思っても、関数の中に接続処理が書かれているので差し替えるのが大変です。
  2. コードが汚くなる: 本来やりたいのは「データの取得」だけなのに、「接続の準備」と「後片付け」のコードが混ざってしまい、読みにくくなります。

2. 依存性注入 (DI) の考え方

ここで登場するのが「依存性注入」です。考え方は非常にシンプルです。

「必要なツール(今回はデータベース接続)は、関数の中で自分で作るのではなく、引数として外から渡してもらう」

これだけです。コードを修正してみましょう。

python
# 【良い例】必要なツールは、引数で渡してもらう

# データベース接続ツールの「型」だけインポート(具体的な接続方法は知らなくていい)
from my_database_lib import Connection

# 変更点:引数に connection を追加!
# 「接続済みのツールを誰か渡してね」という宣言
def get_user_info(user_id: int, connection: Connection):

    # 1. 自分で接続しない!渡されたツールをいきなり使うだけ!
    print("渡された接続ツールを使ってデータを取得します")
    user_data = connection.fetch_user(user_id)

    # 2. 自分で切断もしない!(渡してくれた側の責任)
    return user_data

どうでしょう? 関数の中身が劇的にシンプルになりました。「接続方法」や「後片付け」を気にする必要がなくなり、「データを使って何をするか」という本来の目的に集中できるようになりました。

これが「依存性(=必要なツール)」を外から「注入(=引数で渡す)」する、ということです。

3. FastAPIでの DI の使い方 (Depends)

FastAPIは、この便利な「依存性注入」を、Web APIの仕組みの中で超簡単に使えるようにしてくれています。それが Depends(ディペンズ) です。

FastAPIにおけるDIは、**「面倒な準備と後片付けをフレームワークに丸投げする機能」**と言えます。

実際の使い方

「APIが呼ばれたらデータベース接続を準備して、APIの処理が終わったら接続を切る」という面倒な処理を、FastAPIに自動でやらせてみましょう。

ステップ1: 「準備と後片付け」の処理を定義する

まず、面倒な処理だけを行う関数を作ります。

python
# データベース接続の準備と後片付けを担当する関数
def get_db_connection():
    # 1. 準備(FastAPIが実行)
    print("【自動】DB接続を確立します...")
    db = "(接続済みのDBツールだと思ってください)"

    # 2. 準備したツールをAPI関数に渡す
    yield db

    # 3. 後片付け(FastAPIがAPI処理終了後に実行)
    print("【自動】DB接続を切断します。")
    # db.close() みたいな処理

ステップ2: APIの引数で Depends を使う

APIを定義する関数の引数で、Depends を使って「さっきの関数の機能を使いたい!」と宣言します。

python
from fastapi import FastAPI, Depends

app = FastAPI()

# APIの定義
# 引数 db に注目! Depends(get_db_connection) と書く
@app.get("/users/{user_id}")
def read_user_api(user_id: int, db: str = Depends(get_db_connection)):
    # ここに来た時点で、FastAPIが自動で準備した 'db' が使える状態になっている!

    print(f"APIの中: 渡されたツール「{db}」を使って検索処理をします。")
    # 自分で接続したり切断したりする必要は一切ない!

    return {"user_id": user_id, "name": "テスト太郎"}

何が起きているのか?

  1. ブラウザからAPIにアクセスが来ます。
  2. FastAPIは、API関数を実行するに、Dependsで指定された get_db_connection を実行し、接続ツール(db)を準備します。
  3. 準備したツールを、API関数の引数 db に渡して(注入して)、API関数を実行します。
  4. API関数が値を返した、FastAPIは get_db_connection の残りの処理(後片付け)を実行します。

まとめ

FastAPIの依存性注入(DI)とは、難解な理論ではありません。

  • 「関数の中で必要なツールを自分で準備するのをやめて、引数で受け取るようにする」というシンプルな設計方針のこと。
  • FastAPIの Depends は、そのツールの「準備」と「後片付け」をフレームワークが自動でやってくれる便利な機能。

これを使うと、APIのロジックがすっきりして書きやすくなり、テストも簡単になります。FastAPIを使う大きなメリットの一つなので、ぜひ使ってみてください。