Loading IconFastLaunchAPI
Features

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.py
.env
.env.sample

Settings Class Structure

The Settings class centralizes all configuration management with dynamic OAuth provider detection:

app/config/settings.py
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.

VariableDescriptionExample
SECRET_KEYJWT signing key and general encryptionyour-secret-key-min-32-chars-long-random-string-here
DATABASE_URLPostgreSQL connection string (async)postgresql+asyncpg://user:pass@localhost:5432/dbname
BACKEND_URLBackend API URLhttp://localhost:8000
FRONTEND_URLFrontend application URLhttp://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

VariableDescriptionDefaultExample
REDIS_DSNRedis connection stringNoneredis://:password@redis:6379/0
CORS_ORIGINSAllowed CORS origins["*"]["http://localhost:3000"]

OAuth Configuration

OAuth providers are configured dynamically. Add any provider by setting its CLIENT_ID and CLIENT_SECRET:

.env
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/google
.env
GITHUB_CLIENT_ID=your-github-client-id
GITHUB_CLIENT_SECRET=your-github-client-secret
# Redirect URI is auto-constructed as {BACKEND_URL}/auth/oauth/callback/github
.env
FACEBOOK_CLIENT_ID=your-facebook-client-id
FACEBOOK_CLIENT_SECRET=your-facebook-client-secret
# Redirect URI is auto-constructed as {BACKEND_URL}/auth/oauth/callback/facebook

No 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:

.env
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/.env

Configure Basic Settings

Update the .env file with your specific values:

.env
# 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_URL

Example Usage in Routes

Here's how configuration is used in authentication routes:

app/routers/auth/auth.py
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:

app/routers/auth/oauth_providers.py
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_ID and {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

.env.development
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/0

Docker Environment

.env.docker
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/0

Docker Service Names: In Docker Compose, use service names (postgres, redis) instead of localhost for inter-container communication.

Production Environment

.env.production
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:

  1. Add environment variables:
PROVIDER_CLIENT_ID=your-client-id
PROVIDER_CLIENT_SECRET=your-client-secret
  1. Add to settings.py:
self.PROVIDER_CLIENT_ID: str = os.getenv("PROVIDER_CLIENT_ID")
self.PROVIDER_CLIENT_SECRET: str = os.getenv("PROVIDER_CLIENT_SECRET")
  1. Register in oauth_providers.py - the credentials are automatically detected!

Custom Configuration Values

Extend the Settings class to add custom configuration:

app/config/settings.py
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:

app/config/settings.py
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:

  1. Use strong SECRET_KEY: Generate a secure random key (32+ characters) for production
  2. Environment separation: Use different configurations for dev/staging/production
  3. Secure secrets: Use secret management services (AWS Secrets Manager, Azure Key Vault) in production
  4. Validate inputs: Add validation for critical configuration values
  5. Document defaults: Clearly document default values and requirements
  6. 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

  1. Singleton pattern: Configuration is loaded once at startup
  2. Type hints: Use type hints for better IDE support and validation
  3. Lazy loading: Only load configuration when needed
  4. Caching: Cache expensive configuration computations
  5. 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:

  1. SECRET_KEY not set or too short

    • Solution: Set a secure SECRET_KEY with at least 32 characters
  2. Wrong database driver

    • Error: "No async driver for postgresql://"
    • Solution: Use postgresql+asyncpg:// instead of postgresql://
  3. BACKEND_URL not set

    • Error: OAuth redirect URIs cannot be constructed
    • Solution: Set BACKEND_URL in your .env file
  4. 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}
  5. Redis connection issues in Docker

    • Solution: Use service name redis instead of localhost in Docker Compose

Debug Configuration

Add logging to debug configuration issues:

app/config/settings.py
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_URL

Add BACKEND_URL

Ensure BACKEND_URL is set (required for OAuth):

BACKEND_URL=http://localhost:8000

Update 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

Next Steps