Skip to content

Application settings in one module

The environment and the flags are read in one module of the package, settings.py, through a pydantic_settings.BaseSettings subclass. The rest of the code gets the values from get_settings().

What to read

  • Settings reads the variables with the prefix APP_ and the .env file. case_sensitive=False binds APP_DEBUG and app_debug to the same field. extra="ignore" ignores the entries of the .env file that the class does not declare; the variables of the process that it does not declare are ignored in any case.
  • get_settings is cached with lru_cache: the settings are built and validated once per process. A test calls get_settings.cache_clear() after it changes the environment.
  • An endpoint takes the settings with Depends(get_settings), and a test replaces them with app.dependency_overrides[get_settings]. A module that calls get_settings() when it is imported keeps the first settings and cannot be overridden.

What to adapt

  • The fields and the prefix are an example. Declare the variables of your package, with their types and defaults.
  • Each layer or package has its own settings.py.

References

code/app/settings.py:11-23

the settings class: prefix, .env file, case-insensitive names and typed fields

class Settings(BaseSettings):
    """The variables of the package, read with the prefix `APP_` from the environment and `.env`."""

    model_config = SettingsConfigDict(
        env_prefix="APP_",
        env_file=".env",
        case_sensitive=False,
        extra="ignore",
    )

    debug: bool = False
    log_level: str = "INFO"
    database_url: str = Field(default="sqlite:///./app.db")

code/app/settings.py:26-29

the function that the rest of the code calls: cached, so the settings are built once

@lru_cache
def get_settings() -> Settings:
    """Return the settings of the process, built and validated once."""
    return Settings()

code/app/api/v1/routers/status.py

an endpoint takes the settings as a dependency, so a test replaces them with app.dependency_overrides[get_settings]

"""An endpoint that reads the settings through a dependency, so a test can replace them."""

from typing import Annotated

from fastapi import APIRouter, Depends

from app.api.v1.schemas.responses.status import StatusResponse
from app.settings import Settings, get_settings

router = APIRouter()


@router.get("/status")
def read_status(settings: Annotated[Settings, Depends(get_settings)]) -> StatusResponse:
    """Say whether the service runs in debug mode."""
    return StatusResponse(debug=settings.debug)

https://fastapi.tiangolo.com/advanced/settings/#settings-and-testing

replacing the settings in a test with dependency_overrides

Cited by