Source code for app.server.core

from __future__ import annotations

from contextlib import asynccontextmanager
from typing import TYPE_CHECKING

from asgi_correlation_id import CorrelationIdMiddleware
from fastapi import FastAPI
from fastapi.exceptions import RequestValidationError

from app.__about__ import __version__ as current_version
from app.config.app_settings import alchemy
from app.config.base import get_settings
from app.domain.catalogs.controllers.equipment import equipment_router
from app.domain.catalogs.controllers.exercise_tags import exercise_tags_router
from app.domain.catalogs.controllers.muscle_groups import muscle_router
from app.domain.exercises.controllers import exercise_router
from app.domain.system.controllers import system_router
from app.domain.users.controllers.access import access_router
from app.domain.users.controllers.user_role import role_router
from app.domain.users.controllers.users import users_router
from app.lib.exceptions import BaseAPIException
from app.lib.handlers import (
    http_exception_handler,
    validation_exception_handler,
)
from app.server.lifespan import setup_app_cache
from app.utils.log_utils.middleware import StructLogMiddleware
from app.utils.log_utils.setup import (
    configure_logging,
    start_logging,
    stop_logging,
)

if TYPE_CHECKING:
    from collections.abc import AsyncIterator

    from app.config.base import Settings


[docs] @asynccontextmanager async def lifespan(app: FastAPI, settings: Settings) -> AsyncIterator[None]: """Manage application lifecycle resources. Handles the setup and teardown of essential asynchronous services, including logging queue, Redis client, and application-level caches. Args: app (FastAPI): The main application instance. settings (Settings): The application settings object. Yields: AsyncIterator[None]: Context manager flow control. """ start_logging() setup_app_cache(settings=settings) yield stop_logging()
def _init_error_handlers(app: FastAPI) -> None: """Register custom exception handlers. Args: app (FastAPI): The main application instance. """ app.add_exception_handler(BaseAPIException, http_exception_handler) # type: ignore[arg-type] app.add_exception_handler(RequestValidationError, validation_exception_handler) # type: ignore[arg-type] def _init_routers(app: FastAPI, settings: Settings) -> None: """Include all domain-specific routers into the application. Args: app (FastAPI): The main application instance. settings (Settings): The application settings object. """ app.include_router(access_router, prefix=settings.app.API_V1_URL_PREFIX) app.include_router(users_router, prefix=settings.app.API_V1_URL_PREFIX) app.include_router(role_router, prefix=settings.app.API_V1_URL_PREFIX) app.include_router(equipment_router, prefix=settings.app.API_V1_URL_PREFIX) app.include_router(exercise_tags_router, prefix=settings.app.API_V1_URL_PREFIX) app.include_router(muscle_router, prefix=settings.app.API_V1_URL_PREFIX) app.include_router(exercise_router, prefix=settings.app.API_V1_URL_PREFIX) app.include_router(system_router)
[docs] def create_app() -> FastAPI: """Create and configure the core FastAPI application instance. Returns: FastAPI: The fully configured application instance. """ settings = get_settings() configure_logging() _app = FastAPI( debug=settings.app.DEBUG, title=settings.app.NAME, version=current_version, description=( "Experimental async backend sandbox focused on architectural patterns, " "concurrency, and performance trade-offs within a workout tracking domain." ), lifespan=lambda app: lifespan(app, settings=settings), docs_url="/docs" if settings.app.ENVIRONMENT == "dev" else None, redoc_url="/redoc" if settings.app.ENVIRONMENT == "dev" else None, openapi_url="/openapi.json" if settings.app.ENVIRONMENT == "dev" else None, ) alchemy.init_app(_app) _init_routers(app=_app, settings=settings) _init_error_handlers(app=_app) _app.add_middleware(StructLogMiddleware) _app.add_middleware(CorrelationIdMiddleware) return _app