Building a robust and configurable API requires a clean and maintainable settings management strategy. FastAPI, combined with Pydantic, provides a powerful way to manage application configurations, ensuring that settings are well-structured, validated, and easily accessible. In this post, we’ll explore how to integrate Pydantic settings into a FastAPI application using dependency injection.
Why Use Pydantic Settings in FastAPI?
When developing an API, configurations such as database connections, external services, security keys, and feature toggles need to be accessible throughout the application. Pydantic’s BaseSettings offers:
- Automatic environment variable parsing
- Type validation to prevent incorrect configurations
- Hierarchical settings models for complex applications
- Seamless integration with FastAPI’s dependency injection system
Step 1: Defining Pydantic Settings for FastAPI
We’ll define our application settings using Pydantic’s BaseSettings class.
from pydantic import BaseSettings, Field, PostgresDsn, RedisDsn
from typing import Optional
class AppSettings(BaseSettings):
app_name: str = Field("FastAPI App", env="APP_NAME")
environment: str = Field("development", env="ENVIRONMENT")
debug: bool = Field(True, env="DEBUG")
secret_key: str = Field(..., env="SECRET_KEY")
database_url: PostgresDsn = Field(..., env="DATABASE_URL")
redis_url: Optional[RedisDsn] = Field(None, env="REDIS_URL")
class Config:
env_file = ".env"
This class ensures that:
- Configuration values are fetched from environment variables.
- Type safety is enforced (e.g.,
database_urlmust be a valid PostgreSQL DSN). - Default values can be set (
debug=True). - The
.envfile is used for local development.
Step 2: Injecting Settings into FastAPI Using Dependencies
To make settings available throughout the application, we use FastAPI’s dependency injection system.
from fastapi import Depends, FastAPI
# Load settings once and reuse them
settings = AppSettings()
def get_settings() -> AppSettings:
return settings
app = FastAPI()
@app.get("/settings")
def read_settings(config: AppSettings = Depends(get_settings)):
return {
"app_name": config.app_name,
"environment": config.environment,
"debug": config.debug
}
Why Use Dependency Injection?
- Ensures single-instance reuse instead of creating a new settings object per request.
- Allows for easier testing by overriding dependencies in test cases.
- Provides cleaner separation of concerns, ensuring settings are accessed consistently.
Step 3: Using Pydantic Settings in Database Connections
FastAPI applications often require database connections, and Pydantic settings help in configuring them properly.
from sqlalchemy import create_engine
# Create database engine using Pydantic settings
engine = create_engine(settings.database_url)
This ensures that:
- The database URL is validated at startup.
- Configuration is consistent and easy to manage.
Step 4: Environment-Specific Settings
Different environments (development, staging, production) require different configurations. We can manage them easily with .env files.
.env (Development)
APP_NAME=FastAPI Dev
ENVIRONMENT=development
DEBUG=True
DATABASE_URL=postgresql://dev_user:dev_pass@localhost/dev_db
REDIS_URL=redis://localhost:6379/0
SECRET_KEY=dev-secret-key
.env (Production)
APP_NAME=FastAPI App
ENVIRONMENT=production
DEBUG=False
DATABASE_URL=postgresql://prod_user:prod_pass@db.prod/prod_db
REDIS_URL=redis://cache.prod:6379/0
SECRET_KEY=prod-secret-key
By switching .env files, we can adapt the API configuration dynamically.
Step 5: Overriding Settings for Testing
Testing requires controlled configurations. FastAPI allows dependency overrides for testing purposes.
from fastapi.testclient import TestClient
def get_test_settings() -> AppSettings:
return AppSettings(
app_name="Test App",
environment="testing",
debug=True,
secret_key="test-secret-key",
database_url="postgresql://test_user:test_pass@localhost/test_db"
)
app.dependency_overrides[get_settings] = get_test_settings
client = TestClient(app)
response = client.get("/settings")
print(response.json())
This ensures that:
- Tests run with isolated settings.
- The real application configuration remains unchanged.
Step 6: Security Best Practices
When handling sensitive settings, avoid hardcoding secrets in code:
- Use environment variables instead of embedding credentials in source code.
- Restrict access to
.envfiles in production (chmod 600 .env). - Use cloud secrets managers like AWS Secrets Manager or HashiCorp Vault for storing secrets securely.
Conclusion
Integrating Pydantic settings into FastAPI ensures clean, structured, and secure application configurations. By leveraging dependency injection, environment-based settings, and test overrides, we achieve scalability and maintainability.
Key Takeaways:
✅ Automatic environment variable parsing using BaseSettings
✅ Dependency injection ensures reusable and manageable settings
✅ Database and third-party service configurations stay organized
✅ Environment-based overrides make transitioning between dev/staging/prod seamless
✅ Test-specific settings help ensure reliability without affecting production
By structuring settings this way, FastAPI applications become highly configurable, testable, and secure.

Leave a comment