Configuration Management
Comprehensive guide to managing application settings, environment variables, and configuration in FastLaunchAPI
Configuration Management
FastLaunchAPI uses a centralized configuration system that manages all application settings through environment variables and a singleton Settings class. This approach ensures secure, scalable, and environment-specific configuration management with dynamic OAuth provider support.
Overview
The configuration system is built around the Settings class in app/config/settings.py, which loads environment variables using python-dotenv and provides typed access to all configuration values throughout the application.
All configuration values are loaded once at application startup and cached in
the app_settings singleton instance for optimal performance.
Configuration Architecture
Settings Class Structure
The Settings class centralizes all configuration management with dynamic OAuth provider detection:
import os
from dotenv import load_dotenv
class Settings:
def __init__(self):
load_dotenv()
# Core Application Settings
self.SECRET_KEY: str = os.getenv("SECRET_KEY")
self.BACKEND_URL: str = os.getenv("BACKEND_URL")
self.FRONTEND_URL: str = os.getenv("FRONTEND_URL")
self.REDIS_DSN: str = os.getenv("REDIS_DSN")
# Security Configuration
self.CORS_ORIGINS: list[str] = ["*"]
self.ACCESS_TOKEN_EXPIRATION_DAYS: int = 7
self.REFRESH_TOKEN_EXPIRATION_DAYS: int = 14
# OAuth Providers (add new providers by adding CLIENT_ID and CLIENT_SECRET env vars)
self.GOOGLE_CLIENT_ID: str = os.getenv("GOOGLE_CLIENT_ID")
self.GOOGLE_CLIENT_SECRET: str = os.getenv("GOOGLE_CLIENT_SECRET")
# Add more providers here as needed:
# self.GITHUB_CLIENT_ID: str = os.getenv("GITHUB_CLIENT_ID")
# self.GITHUB_CLIENT_SECRET: str = os.getenv("GITHUB_CLIENT_SECRET")
# self.FACEBOOK_CLIENT_ID: str = os.getenv("FACEBOOK_CLIENT_ID")
# self.FACEBOOK_CLIENT_SECRET: str = os.getenv("FACEBOOK_CLIENT_SECRET")
# Email & Other Services
self.SENDGRID_API_KEY: str = os.getenv("SENDGRID_API_KEY")
self.COMPANY_NAME: str = os.getenv("COMPANY_NAME", "My Company")
self.FROM_EMAIL: str = os.getenv("FROM_EMAIL")
self.SUPPORT_EMAIL: str = os.getenv("SUPPORT_EMAIL")
def get_oauth_credentials(self, provider: str) -> tuple[str | None, str | None]:
"""Get OAuth client ID and secret for a provider."""
provider_upper = provider.upper()
client_id = getattr(self, f"{provider_upper}_CLIENT_ID", None)
client_secret = getattr(self, f"{provider_upper}_CLIENT_SECRET", None)
return client_id, client_secret
# Singleton instance
app_settings = Settings()Dynamic OAuth Support: The get_oauth_credentials() method allows you to
add new OAuth providers by simply adding their environment variables without
modifying the configuration logic.
Environment Variables
Required Variables
These environment variables are required for the application to function properly. Missing values will cause startup failures.
| Variable | Description | Example |
|---|---|---|
SECRET_KEY | JWT signing key and general encryption | your-secret-key-min-32-chars-long-random-string-here |
DATABASE_URL | PostgreSQL connection string (async) | postgresql+asyncpg://user:pass@localhost:5432/dbname |
BACKEND_URL | Backend API URL | http://localhost:8000 |
FRONTEND_URL | Frontend application URL | http://localhost:3000 |
Async Driver Required: Use postgresql+asyncpg:// for the database URL to
enable async SQLAlchemy support. Regular postgresql:// URLs are not
supported.
Optional Variables
| Variable | Description | Default | Example |
|---|---|---|---|
REDIS_DSN | Redis connection string | None | redis://:password@redis:6379/0 |
CORS_ORIGINS | Allowed CORS origins | ["*"] | ["http://localhost:3000"] |
OAuth Configuration
OAuth providers are configured dynamically. Add any provider by setting its CLIENT_ID and CLIENT_SECRET:
GOOGLE_CLIENT_ID=your-google-client-id
GOOGLE_CLIENT_SECRET=your-google-client-secret
# Redirect URI is auto-constructed as {BACKEND_URL}/auth/oauth/callback/googleGITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
# Redirect URI is auto-constructed as {BACKEND_URL}/auth/oauth/callback/githubFACEBOOK_CLIENT_ID=your-facebook-client-id
FACEBOOK_CLIENT_SECRET=your-facebook-client-secret
# Redirect URI is auto-constructed as {BACKEND_URL}/auth/oauth/callback/facebookNo Redirect URI Config Needed: OAuth redirect URIs are automatically
constructed from BACKEND_URL as {BACKEND_URL}/auth/oauth/callback/ {provider}. You only need to add this URL to your OAuth provider's console.
Email Configuration
Configure SendGrid for email services:
SENDGRID_API_KEY=your-sendgrid-api-key
FROM_EMAIL=[email protected]
SUPPORT_EMAIL=[email protected]
COMPANY_NAME="Your Company Name"Environment Setup
Create Environment File
Copy the sample environment file and customize it:
cp backend/.env.sample backend/.envConfigure Basic Settings
Update the .env file with your specific values:
# Core Settings (REQUIRED)
SECRET_KEY=your-secret-key-min-32-chars-long-random-string-here
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/template-db
# Backend & Frontend URLs (REQUIRED)
BACKEND_URL=http://localhost:8000
FRONTEND_URL=http://localhost:3000
# OAuth Providers (Google - add more providers as needed)
GOOGLE_CLIENT_ID=<YOUR_GOOGLE_CLIENT_ID>
GOOGLE_CLIENT_SECRET=<YOUR_GOOGLE_CLIENT_SECRET>
# Stripe Payment
STRIPE_PUBLIC_KEY=<YOUR_STRIPE_PUBLIC_KEY>
STRIPE_SECRET_KEY=<YOUR_STRIPE_SECRET_KEY>
WEBHOOK_SECRET=<YOUR_STRIPE_WEBHOOK_SECRET>
# AI APIs
OPENAI_API_KEY=<YOUR_OPENAI_API_KEY>
GROQ_API_KEY=<YOUR_GROQ_API_KEY>
# Redis (for Celery)
REDIS_DSN=redis://:yourpassword@redis:6379/0
# Email (SendGrid)
SENDGRID_API_KEY=<YOUR_SENDGRID_API_KEY>
SUPPORT_EMAIL=[email protected]
FROM_EMAIL=[email protected]
COMPANY_NAME="Your Company Name"Validate Configuration
The application will validate critical configuration on startup. Missing required variables will cause startup failures with clear error messages.
Using Configuration in Your Code
Importing Settings
Import the settings singleton in your modules:
from app.config.settings import app_settings
# Use configuration values
token_expiration = app_settings.ACCESS_TOKEN_EXPIRATION_DAYS
frontend_url = app_settings.FRONTEND_URL
backend_url = app_settings.BACKEND_URLExample Usage in Routes
Here's how configuration is used in authentication routes:
from app.config.settings import app_settings
from datetime import timedelta
@router.post("/token")
async def login_for_access_token(db: db_dependency, form_data: OAuth2PasswordRequestForm):
user = await authenticate_user(form_data.username, form_data.password, db)
# Using configuration for token expiration
access_token = create_access_token(
user.username,
user.id,
timedelta(days=app_settings.ACCESS_TOKEN_EXPIRATION_DAYS)
)
# Using configuration for refresh token
refresh_token = create_refresh_token(
user.username,
user.id,
timedelta(days=app_settings.REFRESH_TOKEN_EXPIRATION_DAYS)
)
return {"access_token": access_token, "refresh_token": refresh_token, "token_type": "bearer"}Dynamic OAuth Provider Detection
The configuration system supports dynamic OAuth provider detection:
from app.config.settings import app_settings
# Check if a provider is configured
client_id, client_secret = app_settings.get_oauth_credentials('google')
if client_id and client_secret:
# Provider is configured, register it
oauth_providers.register(google_config)Configuration Categories
Core Application Settings
🔧 Core Settings
Essential application configuration
- SECRET_KEY: Used for JWT signing and session encryption (min 32 characters)
- BACKEND_URL: Backend API URL for OAuth redirects and internal references
- FRONTEND_URL: Frontend application URL for redirects after authentication
- REDIS_DSN: Redis connection for Celery task queues and caching
Security Configuration
🔒 Security Settings
Authentication and security parameters
- ACCESS_TOKEN_EXPIRATION_DAYS: JWT access token lifetime (default: 7 days)
- REFRESH_TOKEN_EXPIRATION_DAYS: JWT refresh token lifetime (default: 14 days)
- CORS_ORIGINS: Allowed cross-origin request sources (list of URLs)
OAuth Provider Settings
🔐 OAuth Configuration
Third-party authentication provider settings
- Dynamic Provider Support: Add any OAuth provider by setting
{PROVIDER}_CLIENT_IDand{PROVIDER}_CLIENT_SECRET - Auto-generated Redirect URIs: Redirect URIs are automatically constructed from
BACKEND_URL - Supported Providers: Google, GitHub, Facebook, or any OAuth 2.0 provider
Email Service Configuration
📧 Email Settings
SendGrid email service configuration
- SENDGRID_API_KEY: SendGrid API key for email sending
- FROM_EMAIL: Default sender email address
- SUPPORT_EMAIL: Support email for customer inquiries
- COMPANY_NAME: Company name used in email templates
Environment-Specific Configuration
Development Environment
SECRET_KEY=development-secret-key-change-in-production
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/fastlaunchapi_dev
BACKEND_URL=http://localhost:8000
FRONTEND_URL=http://localhost:3000
REDIS_DSN=redis://localhost:6379/0Docker Environment
SECRET_KEY=your-secret-key-here
DATABASE_URL=postgresql+asyncpg://postgres:postgres@postgres:5432/template-db
BACKEND_URL=http://localhost:8000
FRONTEND_URL=http://localhost:3000
REDIS_DSN=redis://:yourpassword@redis:6379/0Docker Service Names: In Docker Compose, use service names (postgres,
redis) instead of localhost for inter-container communication.
Production Environment
SECRET_KEY=${SECRET_KEY}
DATABASE_URL=${DATABASE_URL}
BACKEND_URL=https://api.yourdomain.com
FRONTEND_URL=https://yourdomain.com
REDIS_DSN=${REDIS_URL}In production, use environment variables from your hosting platform or secure
secret management systems instead of .env files.
Advanced Configuration
Adding New OAuth Providers
To add a new OAuth provider, simply:
- Add environment variables:
PROVIDER_CLIENT_ID=your-client-id
PROVIDER_CLIENT_SECRET=your-client-secret- Add to settings.py:
self.PROVIDER_CLIENT_ID: str = os.getenv("PROVIDER_CLIENT_ID")
self.PROVIDER_CLIENT_SECRET: str = os.getenv("PROVIDER_CLIENT_SECRET")- Register in oauth_providers.py - the credentials are automatically detected!
Custom Configuration Values
Extend the Settings class to add custom configuration:
class Settings:
def __init__(self):
load_dotenv()
# Existing configuration...
# Custom configuration
self.MAX_UPLOAD_SIZE: int = int(os.getenv("MAX_UPLOAD_SIZE", "10485760")) # 10MB
self.RATE_LIMIT_REQUESTS: int = int(os.getenv("RATE_LIMIT_REQUESTS", "100"))
self.RATE_LIMIT_WINDOW: int = int(os.getenv("RATE_LIMIT_WINDOW", "3600")) # 1 hour
# Feature flags
self.ENABLE_REGISTRATION: bool = os.getenv("ENABLE_REGISTRATION", "true").lower() == "true"
self.ENABLE_OAUTH: bool = os.getenv("ENABLE_OAUTH", "true").lower() == "true"Configuration Validation
Add validation to ensure configuration integrity:
import os
from typing import Optional
from dotenv import load_dotenv
class Settings:
def __init__(self):
load_dotenv()
# Load configuration
self.SECRET_KEY: str = os.getenv("SECRET_KEY")
self.DATABASE_URL: str = os.getenv("DATABASE_URL")
self.BACKEND_URL: str = os.getenv("BACKEND_URL")
self.FRONTEND_URL: str = os.getenv("FRONTEND_URL")
# Validate critical settings
self._validate_config()
def _validate_config(self):
"""Validate critical configuration values."""
if not self.SECRET_KEY:
raise ValueError("SECRET_KEY environment variable is required")
if not self.DATABASE_URL:
raise ValueError("DATABASE_URL environment variable is required")
if not self.BACKEND_URL:
raise ValueError("BACKEND_URL environment variable is required")
if not self.FRONTEND_URL:
raise ValueError("FRONTEND_URL environment variable is required")
if len(self.SECRET_KEY) < 32:
raise ValueError("SECRET_KEY must be at least 32 characters long")
if not self.DATABASE_URL.startswith("postgresql+asyncpg://"):
raise ValueError("DATABASE_URL must use async driver (postgresql+asyncpg://)")Configuration Best Practices
Security Guidelines
Never commit sensitive configuration values to version control. Use environment variables or secure secret management systems.
Best Practices:
- Use strong SECRET_KEY: Generate a secure random key (32+ characters) for production
- Environment separation: Use different configurations for dev/staging/production
- Secure secrets: Use secret management services (AWS Secrets Manager, Azure Key Vault) in production
- Validate inputs: Add validation for critical configuration values
- Document defaults: Clearly document default values and requirements
- Async drivers: Always use async database drivers (
postgresql+asyncpg://)
Generating Secure Keys
Generate a secure SECRET_KEY:
# Python
python -c "import secrets; print(secrets.token_urlsafe(32))"
# OpenSSL
openssl rand -base64 32
# PowerShell
[Convert]::ToBase64String([System.Security.Cryptography.RandomNumberGenerator]::GetBytes(32))Performance Considerations
- Singleton pattern: Configuration is loaded once at startup
- Type hints: Use type hints for better IDE support and validation
- Lazy loading: Only load configuration when needed
- Caching: Cache expensive configuration computations
- Dynamic OAuth detection: OAuth providers are detected automatically without hardcoded checks
Troubleshooting
Common Issues
Missing Environment Variables: Ensure all required environment variables are set before starting the application.
Common Problems:
-
SECRET_KEY not set or too short
- Solution: Set a secure SECRET_KEY with at least 32 characters
-
Wrong database driver
- Error: "No async driver for postgresql://"
- Solution: Use
postgresql+asyncpg://instead ofpostgresql://
-
BACKEND_URL not set
- Error: OAuth redirect URIs cannot be constructed
- Solution: Set
BACKEND_URLin your.envfile
-
OAuth not working
- Check: Verify CLIENT_ID and CLIENT_SECRET are set
- Check: Verify redirect URI in provider console matches
{BACKEND_URL}/auth/oauth/callback/{provider}
-
Redis connection issues in Docker
- Solution: Use service name
redisinstead oflocalhostin Docker Compose
- Solution: Use service name
Debug Configuration
Add logging to debug configuration issues:
import logging
import os
from dotenv import load_dotenv
logger = logging.getLogger(__name__)
class Settings:
def __init__(self):
load_dotenv()
# Log configuration loading
logger.info("Loading application configuration...")
self.SECRET_KEY: str = os.getenv("SECRET_KEY")
if not self.SECRET_KEY:
logger.error("SECRET_KEY not found in environment variables")
elif len(self.SECRET_KEY) < 32:
logger.warning("SECRET_KEY is shorter than recommended 32 characters")
self.DATABASE_URL: str = os.getenv("DATABASE_URL")
if self.DATABASE_URL and not self.DATABASE_URL.startswith("postgresql+asyncpg://"):
logger.warning("DATABASE_URL should use async driver (postgresql+asyncpg://)")
logger.info("Configuration loaded successfully")Testing Configuration
Verify your configuration is correct:
# Test script: test_config.py
from app.config.settings import app_settings
print("Configuration Check:")
print(f"✓ SECRET_KEY: {'Set' if app_settings.SECRET_KEY else '✗ Missing'}")
print(f"✓ DATABASE_URL: {'Set' if app_settings.DATABASE_URL else '✗ Missing'}")
print(f"✓ BACKEND_URL: {app_settings.BACKEND_URL or '✗ Missing'}")
print(f"✓ FRONTEND_URL: {app_settings.FRONTEND_URL or '✗ Missing'}")
print(f"✓ REDIS_DSN: {app_settings.REDIS_DSN or '✗ Missing (optional)'}")
# Check OAuth providers
print("\nOAuth Providers:")
for provider in ['google', 'github', 'facebook']:
client_id, client_secret = app_settings.get_oauth_credentials(provider)
status = "✓ Configured" if (client_id and client_secret) else "✗ Not configured"
print(f" {provider.title()}: {status}")Migration from Old Configuration
If you're upgrading from an older version:
Remove GOOGLE_REDIRECT_URI
The GOOGLE_REDIRECT_URI is no longer needed - it's auto-generated from BACKEND_URL:
# Old (remove this)
GOOGLE_REDIRECT_URI=http://localhost:8000/auth/oauth/callback/google
# Not needed anymore! Automatically constructed from BACKEND_URLAdd BACKEND_URL
Ensure BACKEND_URL is set (required for OAuth):
BACKEND_URL=http://localhost:8000Update Database URL
Use async driver:
# Old
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/template-db
# New
DATABASE_URL=postgresql+asyncpg://postgres:postgres@localhost:5432/template-db