FastAPI設定管理を極める:依存性注入でテスト容易なコードを実現
別のモジュールに設定を分離する理由
- テストの容易化
設定をモジュール化することで、テスト時に異なる設定を簡単に適用できます。 - 再利用性
複数のアプリケーションで共通の設定を共有できます。 - コードの整理
設定を別のファイルに分離することで、メインのアプリケーションコードがより整理され、読みやすくなります。
別のモジュールに設定を分離する方法
-
設定用のモジュールを作成
設定を格納するPythonファイル(例:config.py
)を作成します。 -
BaseSettingsを継承したクラスを定義
config.py
内で、PydanticのBaseSettings
を継承したクラスを定義し、設定項目をフィールドとして宣言します。 -
環境変数や.envファイルからの読み込み
BaseSettings
は環境変数や.env
ファイルからの設定読み込みをサポートしています。必要に応じて設定値を環境変数や.env
ファイルに記述します。 -
FastAPIアプリケーションで設定をインポートして使用
メインのFastAPIアプリケーションファイルで、設定モジュールから設定クラスをインポートし、インスタンスを作成して設定値を使用します。
例
config.py
from pydantic import BaseSettings
class Settings(BaseSettings):
app_name: str = "My FastAPI App"
database_url: str = "sqlite:///./test.db"
debug: bool = False
class Config:
env_file = ".env"
settings = Settings()
.env
APP_NAME="My Production App"
DATABASE_URL="postgresql://user:password@host/database"
DEBUG=True
main.py
from fastapi import FastAPI
from config import settings
app = FastAPI()
@app.get("/")
async def root():
return {
"app_name": settings.app_name,
"database_url": settings.database_url,
"debug": settings.debug
}
説明
main.py
では、config.py
からsettings
インスタンスをインポートし、設定値を使用しています。.env
ファイルに設定値を記述することで、環境に応じて設定を変更できます。Config
クラス内で.env
ファイルを指定することで、環境変数に加えて.env
ファイルからも設定を読み込むことができます。config.py
では、Settings
クラスを定義し、app_name
、database_url
、debug
を設定項目として宣言しています。
ポイント
case_sensitive
を使用して、環境変数の大文字と小文字を区別するかどうかを指定できます。env_prefix
を使用して、環境変数のプレフィックスを指定できます。- 設定項目には型アノテーションを付けることで、Pydanticによる型チェックが有効になります。
BaseSettings
は、環境変数、.env
ファイル、コマンドライン引数、設定ファイルなど、さまざまなソースから設定を読み込むことができます。
よくあるエラーとトラブルシューティング
-
- エラー
FileNotFoundError: [Errno 2] No such file or directory: '.env'
- 原因
.env
ファイルが指定されたパスに存在しない、またはパスが間違っている。 - 解決策
.env
ファイルがプロジェクトのルートディレクトリに存在することを確認してください。config.py
のConfig
クラス内のenv_file
変数のパスが正しいことを確認してください。- 絶対パスまたは相対パスを明示的に指定してみてください。
- エラー
-
環境変数の読み込みエラー
- エラー
設定値が環境変数から正しく読み込まれない。 - 原因
- 環境変数が設定されていない。
- 環境変数の名前が間違っている。
- 環境変数の値が期待される型と異なる。
- 環境変数のプレフィックス(
env_prefix
)が間違っている。
- 解決策
- 環境変数が正しく設定されていることを確認してください。
config.py
内の設定フィールド名と環境変数名が一致していることを確認してください。- 型アノテーションが設定値の型と一致していることを確認してください。
env_prefix
を使用している場合は、プレフィックスが正しいことを確認してください。- 大文字小文字の区別をするかの設定(case_sensitive)も確認する。
- エラー
-
設定クラスのインポートエラー
- エラー
ImportError: cannot import name 'Settings' from 'config'
- 原因
config.py
が存在しない。config.py
内のSettings
クラスの名前が間違っている。config.py
のパスが間違っている。
- 解決策
config.py
が存在することを確認してください。config.py
内のSettings
クラスの名前が正しいことを確認してください。main.py
でconfig.py
を正しくインポートしていることを確認してください。
- エラー
-
設定値の型エラー
- エラー
ValidationError
- 原因
- 環境変数や
.env
ファイルの値が、設定フィールドの型アノテーションと一致しない。
- 環境変数や
- 解決策
- 環境変数や
.env
ファイルの値が正しい型であることを確認してください。 config.py
内の型アノテーションを修正してください。
- 環境変数や
- エラー
-
設定の優先順位に関する問題
- エラー
期待される設定値が適用されない。 - 原因
- 環境変数、
.env
ファイル、デフォルト値など、複数の設定ソースが競合している。
- 環境変数、
- 解決策
BaseSettings
の優先順位を理解してください (環境変数 >.env
ファイル > デフォルト値)。- 設定ソースを明確に管理し、競合を避けてください。
- エラー
-
開発環境と本番環境での設定の切り替え
- エラー
開発環境と本番環境で異なる設定を適用できない。 - 原因
- 環境変数や
.env
ファイルが適切に管理されていない。
- 環境変数や
- 解決策
- 開発環境と本番環境で異なる
.env
ファイルを使用してください。 - 環境変数を使用して、環境に応じた設定を適用してください。
- 設定ファイルを環境ごとに分けて管理し、環境変数で切り替えるようにする。
- 開発環境と本番環境で異なる
- エラー
トラブルシューティングのヒント
- テスト
設定に関するユニットテストを作成し、異なる設定値でテストしてください。 - ログ
ロギングを設定して、設定の読み込み状況やエラーメッセージを記録してください。 - デバッグ
print()
文やデバッガを使用して、設定値が正しく読み込まれているか確認してください。
例1: 基本的な設定の分離
config.py
from pydantic import BaseSettings
class Settings(BaseSettings):
app_name: str = "デフォルトのアプリ名"
debug: bool = False
settings = Settings()
main.py
from fastapi import FastAPI
from config import settings
app = FastAPI()
@app.get("/")
async def root():
return {
"app_name": settings.app_name,
"debug": settings.debug,
}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
説明
main.py
でconfig.py
からsettings
インスタンスをインポートし、ルートエンドポイントで設定値を使用しています。config.py
でSettings
クラスを定義し、app_name
とdebug
のデフォルト値を設定しています。
例2: 環境変数と.env
ファイルの使用
config.py
from pydantic import BaseSettings
class Settings(BaseSettings):
app_name: str = "デフォルトのアプリ名"
database_url: str = "sqlite:///./test.db"
debug: bool = False
class Config:
env_file = ".env"
settings = Settings()
.env
APP_NAME="環境変数のアプリ名"
DATABASE_URL="postgresql://user:password@host/mydatabase"
DEBUG=True
main.py
from fastapi import FastAPI
from config import settings
app = FastAPI()
@app.get("/")
async def root():
return {
"app_name": settings.app_name,
"database_url": settings.database_url,
"debug": settings.debug,
}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
説明
- 環境変数の値がデフォルト値を上書きします。
config.py
のConfig
クラスでenv_file = ".env"
を指定することで、.env
ファイルから設定を読み込みます。.env
ファイルに環境変数を設定しています。
例3: 環境変数プレフィックスの使用
config.py
from pydantic import BaseSettings
class Settings(BaseSettings):
app_name: str = "デフォルトのアプリ名"
database_url: str = "sqlite:///./test.db"
debug: bool = False
class Config:
env_prefix = "MYAPP_"
settings = Settings()
環境変数
export MYAPP_APP_NAME="プレフィックス付きアプリ名"
export MYAPP_DATABASE_URL="postgresql://user:password@host/mydatabase"
export MYAPP_DEBUG=True
main.py
from fastapi import FastAPI
from config import settings
app = FastAPI()
@app.get("/")
async def root():
return {
"app_name": settings.app_name,
"database_url": settings.database_url,
"debug": settings.debug,
}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
説明
- プレフィックスを使用することで、環境変数の衝突を避けることができます。
- 環境変数は
MYAPP_
プレフィックス付きで設定する必要があります。 config.py
のConfig
クラスでenv_prefix = "MYAPP_"
を指定することで、環境変数のプレフィックスを設定します。
例4: 複数の設定ファイルの使用
config.py
from pydantic import BaseSettings
import os
class Settings(BaseSettings):
app_name: str = "デフォルトのアプリ名"
database_url: str = "sqlite:///./test.db"
debug: bool = False
class Config:
env_file = os.getenv("ENV_FILE", ".env")
settings = Settings()
.env.dev
APP_NAME="開発環境アプリ名"
DEBUG=True
.env.prod
APP_NAME="本番環境アプリ名"
DATABASE_URL="postgresql://user:password@productionhost/productiondb"
DEBUG=False
環境変数
export ENV_FILE=".env.prod" #本番環境の場合
main.py
from fastapi import FastAPI
from config import settings
app = FastAPI()
@app.get("/")
async def root():
return {
"app_name": settings.app_name,
"database_url": settings.database_url,
"debug": settings.debug,
}
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8000)
os.getenv()
を使うことで環境変数から値を取得することができます。- 開発環境と本番環境で異なる設定ファイルを適用できます。
- 環境変数
ENV_FILE
を使用して、使用する.env
ファイルを切り替えます。
設定ファイルを直接読み込む方法 (ConfigParser, JSON, YAML)
PydanticのBaseSettings
を使用せず、標準ライブラリのconfigparser
やjson
、サードパーティライブラリのPyYAML
などを使用して設定ファイルを直接読み込む方法です。
-
PyYAML (YAMLファイル)
- より複雑な設定を扱うのに適しています。
- 可読性が高い設定ファイルを記述できます。
pip install pyyaml
でインストールする必要があります。
-
json (JSONファイル)
- 構造化された設定を扱うのに適しています。
- 標準ライブラリに含まれているため、追加のインストールは不要です。
-
- シンプルな設定ファイル形式(INI)を扱うのに適しています。
- 標準ライブラリに含まれているため、追加のインストールは不要です。
例 (JSONファイル)
config.json
{
"app_name": "代替アプリ名",
"database_url": "mysql://user:password@host/db",
"debug": true
}
config.py
import json
def load_config(filepath: str):
with open(filepath, "r") as f:
return json.load(f)
config = load_config("config.json")
main.py
from fastapi import FastAPI
from config import config
app = FastAPI()
@app.get("/")
async def root():
return {
"app_name": config["app_name"],
"database_url": config["database_url"],
"debug": config["debug"],
}
環境変数を直接読み込む方法 (os.environ)
os.environ
を使用して、環境変数を直接読み込む方法です。
- 設定の型チェックやデフォルト値の設定は、自分で実装する必要があります。
BaseSettings
を使用せずに、環境変数を直接取得します。
例
config.py
import os
app_name = os.environ.get("APP_NAME", "デフォルトアプリ名")
database_url = os.environ.get("DATABASE_URL", "sqlite:///./test.db")
debug = os.environ.get("DEBUG", "False").lower() == "true"
main.py
from fastapi import FastAPI
from config import app_name, database_url, debug
app = FastAPI()
@app.get("/")
async def root():
return {
"app_name": app_name,
"database_url": database_url,
"debug": debug,
}
設定オブジェクトを自作する方法
独自のクラスを作成して、設定オブジェクトを管理する方法です。
- 設定の検証や変換を柔軟に実装できます。
- より複雑な設定管理が必要な場合に適しています。
例
config.py
class Config:
def __init__(self, app_name, database_url, debug):
self.app_name = app_name
self.database_url = database_url
self.debug = debug
def load_config():
# 環境変数やファイルから設定を読み込む
app_name = "自作アプリ名"
database_url = "postgresql://user:password@host/db"
debug = True
return Config(app_name, database_url, debug)
config = load_config()
main.py
from fastapi import FastAPI
from config import config
app = FastAPI()
@app.get("/")
async def root():
return {
"app_name": config.app_name,
"database_url": config.database_url,
"debug": config.debug,
}
依存性注入 (Dependency Injection) を利用した設定の提供
FastAPIの依存性注入システムを利用して、設定オブジェクトを依存として提供する方法です。
- コードの可読性や保守性が向上します。
- テストや設定の切り替えが容易になります。