Configuration Strategy: Settings and Secrets

Configuration Strategy: Settings and Secrets

Stop hard-coding your API keys. Learn how to use Pydantic Settings and environment variables to manage your application's configuration safely.

Configuration Strategy: Settings and Secrets

One of the most common ways apps get hacked is developers accidentally "committing" their API keys or database passwords to GitHub.

In a production-ready FastAPI app, your code should be generic. The specific details (like which database to connect to) should live in the Environment.


1. Environment Variables: The Source of Truth

An environment variable is a dynamic value that can affect the way running processes behave on a computer.

In your .env file (which you should NEVER commit to Git):

DATABASE_URL=postgresql://user:password@localhost/dbname
API_KEY=sk-123456789
DEBUG=True

2. Pydantic Settings: The Professional Way

FastAPI works perfectly with pydantic-settings. This allows you to define a Python class that automatically reads from your environment variables, converts them to the right type (e.g., a string to a Boolean), and group them together.

The Setup:

from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    app_name: str = "ShShell API"
    admin_email: str
    items_per_user: int = 50

    class Config:
        env_file = ".env"

settings = Settings()

Why this is superior:

  1. Validation: If you forget to set admin_email, the app will crash immediately with a helpful error, rather than failing later when someone tries to send an email.
  2. Type Conversion: If you set DEBUG=True in your .env, Pydantic converts that string into a Python True Boolean.
  3. Global Access: You can import settings anywhere in your app to access your configuration.

3. Handling Different Environments

Your configuration should change based on where your app is running.

/config
    __init__.py
    base.py     # Common settings
    dev.py      # Debug = True, Mock DB
    prod.py     # Debug = False, Real RDS DB

You can choose which config to load using a simple environment flag like ENV_STATE=production.


4. The .env.example Pattern

Since you aren't committing your real .env file, how do other developers know what variables they need to set?

The Solution: Create a .env.example file that contains the variable names but dummy values.

# .env.example
DATABASE_URL=
STRIPE_API_KEY=
DEBUG=False

Visualizing the Config Flow

graph LR
    A[".env File"] --> B["Pydantic BaseSettings"]
    C["System Env Vars"] --> B
    D["Default Values"] --> B
    
    B --> E["Live App Configuration"]
    
    E --> F["Database Connection"]
    E --> G["Security Middleware"]
    E --> H["Frontend URL"]

Summary

  • Never hard-code secrets: Use environment variables.
  • Use Pydantic Settings: It provides validation and type safety for your config.
  • Environment Separation: Make sure your local testing doesn't touch your production database.

In the next lesson, we finish Module 3 with Exercises to test your architecture and configuration knowledge.


Exercise: The Missing Secret

Scenario: You just deployed your app to AWS, but it's crashing with a ValidationError saying SECRET_KEY is required.

  1. Where should you check first?
  2. Why is this crash actually a "Good Thing" compared to the app starting with a default, insecure secret?

Subscribe to our newsletter

Get the latest posts delivered right to your inbox.

Subscribe on LinkedIn