Skip to content

FastAPI configuration

This module defines two building blocks to configure a FastAPI application in a composable, dependency-injection-friendly way:

  • FastApiSettings: a dataclass that carries basic application metadata (title, version, debug) and is designed to be provided by a pico_ioc container.
  • FastApiConfigurer: a protocol (interface) for pluggable components that can mutate/configure a FastAPI app. Multiple configurers can be registered and executed in a deterministic order using a priority.

Use these together to bootstrap your app with clear separation between application settings and configuration steps such as adding middleware, routers, and event handlers.

FastApiSettings

What it is: - An immutable dataclass that holds basic FastAPI settings. - Intended to be injected where needed using pico_ioc.

Fields: - title: str — The application title (propagated to FastAPI). - version: str — The application version (propagated to FastAPI). - debug: bool — Whether to run FastAPI in debug mode.

How to use: - Register a single FastApiSettings instance in your pico_ioc container. - Use it when instantiating FastAPI, or inject into configurers for conditional behavior.

Example:

from fastapi import FastAPI
from pico_ioc import Container
from yourpkg.config import FastApiSettings  # adjust import path

# 1) Compose DI container with settings
container = Container()
container.register_instance(FastApiSettings(
    title="Example API",
    version="1.0.0",
    debug=True,
))

# 2) Resolve settings and use them to build the app
settings = container.resolve(FastApiSettings)
app = FastAPI(
    title=settings.title,
    version=settings.version,
    debug=settings.debug,
)

FastApiConfigurer protocol

What it is: - A protocol defining a uniform way to configure a FastAPI app. - Lets you register multiple independent configurers and run them in a defined order.

Members: - priority(self) -> int: Returns a numeric priority used to sort configurers. Use this to control execution order. - configure(self, app) -> None: Receives the FastAPI app instance and performs configuration (e.g., include routers, add middleware, register events).

Typical use cases: - Add middleware (CORS, logging, tracing). - Register routers and dependencies. - Mount sub-apps and static files. - Register startup/shutdown event handlers.

Example: implement and register configurers

from fastapi import FastAPI
from pico_ioc import Container
from yourpkg.config import FastApiConfigurer, FastApiSettings  # adjust import path

# A configurer that sets up an API router
class ApiRouterConfigurer(FastApiConfigurer):
    def priority(self) -> int:
        # Run after low-level middleware, before docs tweaks
        return 200

    def configure(self, app: FastAPI) -> None:
        from fastapi import APIRouter
        router = APIRouter()

        @router.get("/health")
        def health():
            return {"status": "ok"}

        app.include_router(router, prefix="/api")

# A configurer that adds a simple middleware
class LoggingMiddlewareConfigurer(FastApiConfigurer):
    def priority(self) -> int:
        # Run early to capture as much as possible
        return 100

    def configure(self, app: FastAPI) -> None:
        @app.middleware("http")
        async def log_requests(request, call_next):
            response = await call_next(request)
            # Add your logging here
            return response

# A configurer that depends on settings (DI via pico_ioc)
class DebugOnlyDocsConfigurer(FastApiConfigurer):
    def __init__(self, settings: FastApiSettings) -> None:
        self._settings = settings

    def priority(self) -> int:
        return 300

    def configure(self, app: FastAPI) -> None:
        # Example of conditional behavior based on settings
        if not self._settings.debug:
            # Disable Swagger UI in non-debug environments
            app.docs_url = None
            app.redoc_url = None

Register and apply configurers with the container:

from fastapi import FastAPI
from pico_ioc import Container
from yourpkg.config import FastApiSettings, FastApiConfigurer  # adjust import path

container = Container()

# Settings instance
container.register_instance(FastApiSettings(
    title="Example API",
    version="1.0.0",
    debug=True,
))

# Register configurers; exact API depends on pico_ioc, adjust as needed
container.register(FastApiConfigurer, LoggingMiddlewareConfigurer)
container.register(FastApiConfigurer, ApiRouterConfigurer)
container.register(FastApiConfigurer, DebugOnlyDocsConfigurer)

# Build the app
settings = container.resolve(FastApiSettings)
app = FastAPI(
    title=settings.title,
    version=settings.version,
    debug=settings.debug,
)

# Resolve all configurers and apply them in priority order
# Replace `resolve_all` with the appropriate pico_ioc API if different
configurers = container.resolve_all(FastApiConfigurer)
configurers = sorted(configurers, key=lambda c: c.priority())

for cfg in configurers:
    cfg.configure(app)

Notes on priority: - Priority is used solely to order configurers. Choose a consistent convention, e.g., smaller numbers run earlier. - Consider grouping numbers by concern (100 for core/middleware, 200 for routers, 300 for documentation, etc.) to leave room for expansion.

Testing a configurer:

from fastapi import FastAPI

def test_api_router_configurer_adds_routes():
    app = FastAPI()
    cfg = ApiRouterConfigurer()
    cfg.configure(app)

    routes = {r.path for r in app.routes}
    assert "/api/health" in routes

Summary: - FastApiSettings provides injectable core settings for FastAPI instantiation. - FastApiConfigurer defines a simple contract for modular, ordered app configuration. - Register settings and configurers in pico_ioc, construct the app from settings, then resolve and execute all configurers in priority order.