FastAPI: From Basics to Production · Pydantic v2 in Depth

Configuration with pydantic-settings

Load typed settings from environment variables and .env files.

pydantic-settings loads configuration from environment variables (and optionally .env files) into a typed BaseSettings class, validating it at startup. Use SecretStr for secrets (masked in repr/logs), prefixes to namespace variables, and provide settings via a cached dependency so tests can override them.

from functools import lru_cache
from typing import Annotated
from fastapi import Depends
from pydantic import PostgresDsn, SecretStr
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file=".env", env_prefix="APP_")

    database_url: PostgresDsn
    jwt_secret: SecretStr
    access_token_minutes: int = 15
    debug: bool = False


@lru_cache
def get_settings() -> Settings:
    return Settings()


SettingsDep = Annotated[Settings, Depends(get_settings)]
# APP_DATABASE_URL=postgresql+asyncpg://... APP_JWT_SECRET=...

Common pitfalls

  • Reading os.environ all over the codebase.
  • Logging settings objects containing secrets as plain strings.
  • Committing .env with real secrets.

Key points

  • Typed, validated config at startup
  • SecretStr for secrets
  • Cached dependency, overridable in tests

Further reading: FastAPI: Settings and environment variables

Practice questions

Why wrap get_settings() with lru_cache?

Answer

So the environment and .env file are read and validated once, not on every request, while still allowing tests to override the dependency.

What does SecretStr protect against?

Answer

Accidental exposure: its repr and str show **********; you must call .get_secret_value() explicitly to use it.

Open in the app to mark lessons complete, take notes and track your progress.