别把数据库地址和密钥写死:FastAPI 配置管理实践
摘要:使用 pydantic-settings 把环境名称、数据库 URL、调试开关和密钥变成有类型的配置对象,并通过依赖覆盖测试配置、通过响应模型阻止秘密泄漏。

图:环境差异通过配置注入应用,敏感密钥始终留在受保护的边界内。
本地使用 SQLite,测试使用内存库,生产连接 PostgreSQL;这三种环境不应该靠改源码切换。更不能把真实密钥复制进仓库,再期待部署时有人记得修改。
这篇文章把可变配置集中到 Settings,统一使用 TASKS_ 环境变量前缀,并专门验证一件容易被忽略的事:配置对象可以进入应用,但敏感字段不能进入公开响应。
先看环境变量怎样改变应用行为
TASKS_APP_NAME="我的任务 API" TASKS_DEBUG=true \
fastapi dev examples/ch08_config/main.py
curl http://127.0.0.1:8000/info
预期响应包含应用名、环境和 debug,不包含 secret_key 与 database_url。
先准备一份不含真实秘密的示例配置
cd 04-fastapi-beginner
source .venv/bin/activate
cp examples/ch08_config/.env.example .env
.env 已加入 .gitignore。提交的是不含真实秘密的 .env.example,真正的生产配置应由部署环境注入。
把环境变量收进 Settings
声明 Settings
class Settings(BaseSettings):
app_name: str = "任务管理 API"
environment: str = "development"
debug: bool = False
database_url: str = "sqlite:///./tasks.db"
secret_key: str = "change-me-in-production"
model_config = SettingsConfigDict(
env_file=".env",
env_prefix="TASKS_",
extra="ignore",
)
字段类型同样负责转换,例如环境变量文本 true 会变为 Python 布尔值。
缓存并注入配置
@lru_cache
def get_settings() -> Settings:
return Settings()
SettingsDep = Annotated[Settings, Depends(get_settings)]
配置通常在进程生命周期内稳定,缓存可以避免重复读取 .env。测试替换依赖时不需要修改全局环境。
只公开安全字段
class PublicSettings(BaseModel):
app_name: str
environment: str
debug: bool
@app.get("/info", response_model=PublicSettings)
def read_info(settings: SettingsDep) -> Settings:
return settings
即使内部对象包含秘密,响应模型也只允许公开字段。

图:原始环境变量进入 Settings 类型边界,完成转换和校验后再注入应用。
切换环境变量并检查公开响应
curl http://127.0.0.1:8000/info
TASKS_DEBUG=false python -c \
"from examples.ch08_config.main import Settings; print(Settings().debug)"
python -m pytest tests/test_ch08.py -q
预期第二条输出 False,测试证明敏感字段未泄漏且配置依赖可替换。
完成后可删除项目根目录临时 .env;示例文件仍保留。
Settings 也是一道有类型的输入边界
配置优先级需要团队明确:代码默认值适合本地安全默认,.env 适合个人开发,真实环境变量适合 CI 和部署平台。敏感值永远不应作为示例默认值投入生产。
Settings 是应用输入边界,与请求模型类似:外部文本先经过类型转换和校验,再被业务代码使用。配置失败应该在启动期暴露,而不是处理第一个真实请求时才发现。
配置管理最危险的不是类型错误,而是泄密
- 提交真实
.env:一旦进入 Git 历史,仅删除文件并不足够,还必须轮换秘密。 - 环境变量名称没有前缀:容易与系统或其他应用变量冲突。
- 把 Settings 直接作为公开响应模型:可能泄漏数据库地址和密钥。
- 修改环境变量后缓存仍是旧值:测试中可调用
get_settings.cache_clear(),生产通常重启进程。 - 默认密钥进入生产:部署检查必须拒绝
change-me-in-production。

图:敏感配置留在应用内部,公开响应只能穿过安全边界输出普通信息。
让生产环境拒绝默认密钥
- 增加
log_level,只允许 DEBUG、INFO、WARNING、ERROR。 - 当 environment=production 且 secret_key 为默认值时,让 Settings 校验失败。
- 使用 monkeypatch 编写环境变量优先级测试。
- 检查
git status,确认.env不会被跟踪。
最后,用测试固定这次改动
手动请求通过后,运行与本文对应的自动化测试:
python -m pytest tests/test_ch08.py -q
python tests/validate_course.py
写在最后
配置已经从源码中解耦,下一篇会通过配置连接 SQLite,并使用依赖为每个请求提供独立数据库 Session,把内存 CRUD 替换为真实持久化。