Skip to content

FastAPI Settings

Application Kit provides a settings system for FastAPI applications that bridges Bender configuration with a type-safe Settings object.

Overview

The settings system allows you to:

  • Define service-specific configuration by subclassing Settings
  • Bridge Bender manifest values to FastAPI settings via get_configuration()
  • Access settings dynamically via get_settings()
  • Override settings in tests without modifying code

Bridging Bender Config to FastAPI

The key pattern is to declare settings attributes, then populate them from Bender configuration at startup:

"""FastAPI settings configuration example.

This example shows how to subclass Settings, register it, and populate
values from Bender configuration in your application's __init__.py.
"""

import os

from application_kit.fastapi.settings import Settings, get_settings, register_settings
from application_kit.settings import get_configuration


class MyServiceSettings(Settings):
    """Custom settings for my service.

    Declare attributes without defaults - they will be configured at startup.
    These values can be accessed via get_settings() throughout your app.
    """

    # Service-specific settings (populated from Bender manifest)
    MAX_RESULTS_PER_PAGE: int
    CACHE_TTL_SECONDS: int
    EXTERNAL_API_KEY: str

    # Environment-based settings
    DEVELOPMENT: bool


# Register the settings instance
register_settings(MyServiceSettings())

# Bridge Bender configuration to FastAPI settings
get_settings().configure("MAX_RESULTS_PER_PAGE", get_configuration("MAX_RESULTS_PER_PAGE"))
get_settings().configure("CACHE_TTL_SECONDS", get_configuration("CACHE_TTL_SECONDS"))
get_settings().configure("EXTERNAL_API_KEY", get_configuration("EXTERNAL_API_KEY"))

# Environment variables
get_settings().configure("DEVELOPMENT", os.environ.get("DEVELOPMENT", "no") == "yes")

This bridges two systems:

Source Method Example
Bender manifest get_configuration() Database URLs, API keys, feature flags
Environment variables os.environ.get() DEVELOPMENT, DEBUG

Bender Manifest Configuration

Add your settings to application.json:

{
  "configurations": [
    {"name": "MAX_RESULTS_PER_PAGE", "type": "int", "test_value": 100},
    {"name": "CACHE_TTL_SECONDS", "type": "int", "test_value": 300},
    {"name": "EXTERNAL_API_KEY", "type": "string", "test_value": "test-key"}
  ]
}

Using Settings

Always fetch settings dynamically

Never store settings in module-level variables or class attributes. Always call get_settings() when you need a value. This ensures test overrides work correctly.

"""FastAPI settings usage example.

Shows how to access settings and configuration in your application code.
"""

from bender.shared.manifest.types import RateLimitMode
from fastapi import APIRouter

from application_kit.fastapi.settings import Settings, get_settings
from application_kit.settings import get_configuration

router = APIRouter()


@router.get("/search")
async def search() -> dict[str, str | None]:
    """Example endpoint that uses settings.

    Always call get_settings() when you need settings values.
    This ensures overrides (e.g., in tests) are respected.
    """
    # Fetch settings dynamically each time
    settings = get_settings()

    return {
        "product": settings.PRODUCT.value if settings.PRODUCT else None,
    }


# BAD: Don't do this - settings won't reflect runtime overrides
# _cached_settings = get_settings()  # WRONG!


# Configuration values from manifest
def is_rate_limit_disabled() -> bool:
    """Check if rate limiting is disabled using manifest configuration."""
    mode: RateLimitMode = get_configuration("RATE_LIMIT_MODE")
    return mode == RateLimitMode.off


# Custom Settings subclass example
class MyAppSettings(Settings):
    """Custom settings for your application."""

    MY_CUSTOM_SETTING: str = "default_value"

Built-in Settings

The base Settings class provides these settings:

Setting Type Default Description
PRODUCT Products \| None None Default product for authentication
DISABLE_RATE_LIMIT bool False Disable rate limiting globally

Testing with Settings

Override settings in tests using the decorator or context manager:

"""FastAPI settings testing example.

Shows how to override settings in tests.
"""

from collections.abc import Generator

import pytest

from application_kit.fastapi.settings import SettingsOverride, override_settings


@override_settings(DISABLE_RATE_LIMIT=True)
def test_with_rate_limit_disabled() -> None:
    """Use the decorator to override settings for a test function."""
    # Rate limiting is disabled for this test
    pass


def test_with_context_manager() -> None:
    """Use the context manager for fine-grained control."""
    # Default settings apply here

    with SettingsOverride(DISABLE_RATE_LIMIT=True):
        # Rate limiting disabled in this block
        pass

    # Default settings apply again


@pytest.fixture
def disabled_rate_limit() -> Generator[None, None, None]:
    """Fixture that disables rate limiting."""
    with SettingsOverride(DISABLE_RATE_LIMIT=True):
        yield

Best Practices for Testing

  1. Use @override_settings decorator for test functions that need settings changed for the entire test
  2. Use SettingsOverride context manager when you need to test both default and overridden behavior
  3. Create fixtures for commonly overridden settings

API Reference

application_kit.fastapi.settings

Settings

Settings()
Source code in application_kit/fastapi/settings.py
12
13
def __init__(self) -> None:
    self._overrides: dict[str, Any] = {}

SettingsOverride

SettingsOverride(**overrides)
Source code in application_kit/fastapi/settings.py
36
37
def __init__(self, **overrides: Any) -> None:
    self.overrides = overrides

register_settings

register_settings(settings)

Used to register a subclass of settings with additional configurations

Source code in application_kit/fastapi/settings.py
56
57
58
def register_settings(settings: Settings) -> None:
    """Used to register a subclass of settings with additional configurations"""
    settings_context.settings = settings

get_settings

get_settings()
Source code in application_kit/fastapi/settings.py
61
62
def get_settings() -> Settings:
    return settings_context.settings

override_settings

override_settings(**overrides)
Source code in application_kit/fastapi/settings.py
65
66
67
68
69
70
71
72
73
74
def override_settings(**overrides: Any) -> Callable[[Callable[..., Any]], Callable[..., Any]]:
    def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
        @functools.wraps(func)
        def wrapper(*args: Any, **kwargs: Any) -> Any:
            with SettingsOverride(**overrides):
                return func(*args, **kwargs)

        return wrapper

    return decorator