Files
fastAPI/docs/plans/2026-05-12-yaml-configuration.md
Misaka_Company 71d261af26 docs: add YAML configuration implementation plan
Add detailed step-by-step implementation plan for migrating
from .env to YAML configuration using pydantic-yaml.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-12 09:26:26 +08:00

10 KiB

YAML Configuration Implementation Plan

For Claude: REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.

Goal: Migrate FastAPI configuration from .env to YAML using pydantic-yaml

Architecture: Create new config/ module with YAML-based settings using pydantic-yaml, preserving type validation and improving readability through nested configuration structure.

Tech Stack: pydantic-yaml, pyyaml, pytest


Task 1: Add pydantic-yaml Dependency

Files:

  • Modify: requirements.txt

Step 1: Add pydantic-yaml to requirements.txt

Add this line to requirements.txt:

pydantic-yaml>=0.12.0

Step 2: Install the dependency

Run: .venv/Scripts/pip install pydantic-yaml

Expected: Successfully installs pydantic-yaml and its dependencies

Step 3: Verify installation

Run: .venv/Scripts/python -c "import pydantic_yaml; print(pydantic_yaml.__version__)"

Expected: Prints version number without errors

Step 4: Commit

git add requirements.txt
git commit -m "deps: add pydantic-yaml for YAML configuration support"

Task 2: Create config Directory Structure

Files:

  • Create: config/__init__.py
  • Create: config/settings.py
  • Create: config/settings.yaml

Step 1: Create config directory

Run: mkdir config

Step 2: Create config/init.py

from config.settings import settings, Settings, load_settings

__all__ = ["settings", "Settings", "load_settings"]

Step 3: Create config/settings.py with configuration classes

from pathlib import Path
from sqlalchemy.engine import URL
from pydantic import BaseModel
from pydantic_yaml import YamlModel


class SqlServerConfig(BaseModel):
    """SQL Server 连接配置"""
    host: str
    port: int = 1433
    database: str
    username: str
    password: str
    driver: str = "{ODBC Driver 18 for SQL Server}"
    trust_server_certificate: str = "yes"


class Settings(YamlModel):
    """应用配置"""
    database: SqlServerConfig

    @property
    def database_url(self) -> URL:
        """构建数据库连接 URL"""
        conf = self.database.sql_server
        return URL.create(
            "mssql+pyodbc",
            username=conf.username,
            password=conf.password,
            host=conf.host,
            port=conf.port,
            database=conf.database,
            query={
                "driver": conf.driver.strip("{}"),
                "TrustServerCertificate": conf.trust_server_certificate,
            },
        )


def load_settings(config_path: str = "config/settings.yaml") -> Settings:
    """加载 YAML 配置文件"""
    path = Path(config_path)
    if not path.exists():
        raise FileNotFoundError(
            f"配置文件不存在: {config_path}\n"
            f"请确保文件存在于项目根目录或指定正确路径"
        )
    return Settings.parse_yaml_file(path)


# 全局配置单例
settings = load_settings()

Step 4: Create config/settings.yaml with current configuration

# 数据库配置
database:
  sql_server:
    host: 192.168.110.114
    port: 1433
    database: CompanyDB
    username: peng
    password: Cqbld123456.
    driver: "{ODBC Driver 18 for SQL Server}"
    trust_server_certificate: yes

Step 5: Commit

git add config/
git commit -m "feat: add YAML configuration module with pydantic-yaml"

Task 3: Write Configuration Loading Tests

Files:

  • Create: tests/test_config.py
  • Create: tests/fixtures/test_config.yaml

Step 1: Create tests/fixtures directory

Run: mkdir tests/fixtures

Step 2: Create tests/fixtures/test_config.yaml

# 测试配置
database:
  sql_server:
    host: localhost
    port: 1433
    database: TestDB
    username: test_user
    password: test_pass
    driver: "{ODBC Driver 18 for SQL Server}"
    trust_server_certificate: yes

Step 3: Write the failing test for settings loading

Create tests/test_config.py:

import pytest
from config.settings import load_settings


def test_load_settings_success():
    """测试成功加载配置文件"""
    settings = load_settings("tests/fixtures/test_config.yaml")
    assert settings.database.sql_server.port == 1433
    assert settings.database.sql_server.host == "localhost"
    assert settings.database.sql_server.database == "TestDB"


def test_load_settings_file_not_found():
    """测试配置文件不存在时抛出异常"""
    with pytest.raises(FileNotFoundError):
        load_settings("nonexistent.yaml")


def test_database_url_property():
    """测试 database_url 属性生成"""
    settings = load_settings("tests/fixtures/test_config.yaml")
    url = settings.database_url
    assert "mssql+pyodbc" in str(url)
    assert "test_user" in str(url)
    assert "TestDB" in str(url)

Step 4: Run tests to verify they pass

Run: .venv/Scripts/pytest tests/test_config.py -v

Expected: All tests PASS

Step 5: Commit

git add tests/test_config.py tests/fixtures/test_config.yaml
git commit -m "test: add configuration loading tests"

Task 4: Update database.py to Use New Config

Files:

  • Modify: app/core/database.py

Step 1: Read current database.py to understand usage

Check the current import and usage of settings

Step 2: Update import statement

Change:

from app.core.config import settings

To:

from config.settings import settings

Step 3: Verify the rest of the code works unchanged

The database_url property should work exactly as before

Step 4: Run existing tests to ensure no breakage

Run: .venv/Scripts/pytest tests/ -v

Expected: All existing tests still PASS

Step 5: Commit

git add app/core/database.py
git commit -m "refactor: update database.py to use new config module"

Task 5: Update API Module Imports

Files:

  • Modify: app/api/v1/box.py
  • Modify: app/api/v1/location.py
  • Modify: app/services/box_service.py
  • Modify: app/services/location_service.py

Step 1: Check all files that import from app.core.config

Run: grep -r "from app.core.config import" app/

Step 2: Update each file's import

Change:

from app.core.config import settings

To:

from config.settings import settings

Step 3: Run tests after each file change

Run: .venv/Scripts/pytest tests/ -v

Expected: All tests PASS

Step 4: Commit all import changes

git add app/api/v1/box.py app/api/v1/location.py app/services/
git commit -m "refactor: update all imports to use new config module"

Task 6: Update Test Fixtures

Files:

  • Modify: tests/conftest.py

Step 1: Read current conftest.py

Check for any references to app.core.config

Step 2: Update imports in conftest.py

Change any:

from app.core.config import settings

To:

from config.settings import settings

Step 3: Run tests

Run: .venv/Scripts/pytest tests/ -v

Expected: All tests PASS

Step 4: Commit

git add tests/conftest.py
git commit -m "test: update conftest imports"

Task 7: Update Main Application Entry Point

Files:

  • Modify: app/main.py

Step 1: Read current main.py

Check for any config-related initialization

Step 2: Add error handling for configuration loading

Add at the top of main.py or update existing initialization:

import sys
from config.settings import load_settings

try:
    settings = load_settings()
except FileNotFoundError as e:
    print(f"配置文件错误: {e}")
    sys.exit(1)
except Exception as e:
    print(f"加载配置失败: {e}")
    sys.exit(1)

Step 3: Test application startup

Run: .venv/Scripts/python -m app.main

Expected: Application starts without errors

Step 4: Commit

git add app/main.py
git commit -m "feat: add config error handling on startup"

Task 8: Remove Old Configuration Files

Files:

  • Delete: app/core/config.py
  • Delete: .env

Step 1: Verify no remaining imports of old config

Run: grep -r "from app.core.config import" .

Expected: No results (or only in .git directory)

Step 2: Remove old config.py file

Run: rm app/core/config.py

Step 3: Remove .env file

Run: rm .env

Step 4: Add .env to .gitignore if not already present

Ensure .env is in .gitignore

Step 5: Run final test suite

Run: .venv/Scripts/pytest tests/ -v

Expected: All tests PASS

Step 6: Run application to verify

Run: .venv/Scripts/python -m app.main

Expected: Application starts successfully

Step 7: Commit cleanup

git add app/core/config.py .env .gitignore
git commit -m "refactor: remove old .env and config.py files"

Task 9: Update .gitignore for YAML Config

Files:

  • Modify: .gitignore (if it exists, otherwise create)

Step 1: Check if .gitignore exists

Run: ls -la .gitignore

Step 2: Add or verify config/settings.local.yaml is ignored

Add to .gitignore:

# Local configuration overrides
config/settings.local.yaml

This allows developers to have local overrides without committing them

Step 3: Commit

git add .gitignore
git commit -m "chore: ignore local YAML config overrides"

Task 10: Final Verification and Documentation

Files:

  • Update: README.md (if exists)

Step 1: Run complete test suite

Run: .venv/Scripts/pytest tests/ -v --cov=app --cov-report=term-missing

Expected: All tests pass, coverage maintained

Step 2: Start application and verify database connection

Run: .venv/Scripts/python -m app.main

Expected: Application connects to database successfully

Step 3: Update README.md with configuration instructions

Add to README.md:

## Configuration

Configuration is managed via YAML files in the `config/` directory.

### Configuration File

Edit `config/settings.yaml` to configure your environment:

```yaml
database:
  sql_server:
    host: your-host
    port: 1433
    database: your-database
    username: your-username
    password: your-password

Local Overrides

For local development, create config/settings.local.yaml to override specific values without committing them.


**Step 4: Final commit**

```bash
git add README.md
git commit -m "docs: update README with YAML configuration instructions"

Verification Checklist

After completing all tasks:

  • All tests pass: pytest tests/ -v
  • Application starts successfully
  • Database connection works
  • No references to app.core.config remain
  • .env file removed
  • YAML config file exists and is valid
  • README updated with configuration instructions