Configuration Management
Use pydantic-settings for type-safe configuration from environment variables.
My API has hardcoded values everywhere — database URLs, API keys, feature flags. How do I manage these properly?
With environment variables and pydantic-settings. The idea: all configuration lives outside your code, loaded from environment variables or .env files. Different environments (dev, staging, prod) use different values without changing a single line of code.
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_url: str = "sqlite:///dev.db"
api_key: str
debug: bool = False
max_connections: int = 10
model_config = {"env_file": ".env"}
settings = Settings()
BaseSettings reads values from environment variables automatically. DATABASE_URL=postgres://... in your environment becomes settings.database_url. It's case-insensitive and type-validated.
What if a required variable is missing?
Pydantic raises a validation error at startup — before your app handles any requests. Fields without defaults are required:
class Settings(BaseSettings):
api_key: str # Required — crash if missing
debug: bool = False # Optional — defaults to False
This is called fail-fast. Better to crash on startup with a clear error than to crash on the first request when you try to use a missing key.
How do I use this with FastAPI?
Create the settings once and inject them via dependency:
from functools import lru_cache
@lru_cache()
def get_settings():
return Settings()
@app.get("/config")
def show_config(settings: Settings = Depends(get_settings)):
return {
"debug": settings.debug,
"max_connections": settings.max_connections
}
@lru_cache() ensures Settings is created only once and reused. The .env file is read once at startup, not on every request.
What about secrets? I don't want API keys in my .env file in production.
In production, set environment variables directly — through your deployment platform (Railway, Vercel, AWS). The .env file is for local development only:
# .env — local development only, NEVER commit this
DATABASE_URL=sqlite:///dev.db
API_KEY=dev-key-123
DEBUG=true
Add .env to your .gitignore. Production secrets come from the platform's environment variable settings, not files. Your code doesn't care where the values come from — BaseSettings reads them the same way.
Practice your skills
Sign up to write and run code in this lesson.
Configuration Management
Use pydantic-settings for type-safe configuration from environment variables.
My API has hardcoded values everywhere — database URLs, API keys, feature flags. How do I manage these properly?
With environment variables and pydantic-settings. The idea: all configuration lives outside your code, loaded from environment variables or .env files. Different environments (dev, staging, prod) use different values without changing a single line of code.
from pydantic_settings import BaseSettings
class Settings(BaseSettings):
database_url: str = "sqlite:///dev.db"
api_key: str
debug: bool = False
max_connections: int = 10
model_config = {"env_file": ".env"}
settings = Settings()
BaseSettings reads values from environment variables automatically. DATABASE_URL=postgres://... in your environment becomes settings.database_url. It's case-insensitive and type-validated.
What if a required variable is missing?
Pydantic raises a validation error at startup — before your app handles any requests. Fields without defaults are required:
class Settings(BaseSettings):
api_key: str # Required — crash if missing
debug: bool = False # Optional — defaults to False
This is called fail-fast. Better to crash on startup with a clear error than to crash on the first request when you try to use a missing key.
How do I use this with FastAPI?
Create the settings once and inject them via dependency:
from functools import lru_cache
@lru_cache()
def get_settings():
return Settings()
@app.get("/config")
def show_config(settings: Settings = Depends(get_settings)):
return {
"debug": settings.debug,
"max_connections": settings.max_connections
}
@lru_cache() ensures Settings is created only once and reused. The .env file is read once at startup, not on every request.
What about secrets? I don't want API keys in my .env file in production.
In production, set environment variables directly — through your deployment platform (Railway, Vercel, AWS). The .env file is for local development only:
# .env — local development only, NEVER commit this
DATABASE_URL=sqlite:///dev.db
API_KEY=dev-key-123
DEBUG=true
Add .env to your .gitignore. Production secrets come from the platform's environment variable settings, not files. Your code doesn't care where the values come from — BaseSettings reads them the same way.
Practice your skills
Sign up to write and run code in this lesson.