Files
WealthWise/backend/app/main.py

131 lines
3.9 KiB
Python

"""WealthWise FastAPI application factory.
This module initializes and configures the FastAPI application with:
- Middleware configuration (CORS, logging, error handling)
- API router registration
- Lifecycle event handlers (startup/shutdown)
- Exception handlers
"""
import time
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request, status
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.gzip import GZipMiddleware
from fastapi.responses import JSONResponse
from app.api.v1.api import api_router
from app.core.config import get_settings
from app.core.db import check_db_connection, close_engine
settings = get_settings()
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Application lifespan context manager.
Handles startup and shutdown events:
- Startup: Verify database connectivity, initialize caches
- Shutdown: Close database connections, cleanup resources
Args:
app: FastAPI application instance
Yields:
None: Application runs during this period
"""
# Startup
print(f"Starting {settings.PROJECT_NAME} v{settings.VERSION}")
# Verify database connectivity on startup
db_healthy = await check_db_connection()
if not db_healthy:
print("WARNING: Database connection failed on startup!")
else:
print("Database connection established")
yield
# Shutdown
print(f"Shutting down {settings.PROJECT_NAME}")
await close_engine()
print("Database connections closed")
def create_application() -> FastAPI:
"""Create and configure FastAPI application.
Returns:
Configured FastAPI application instance
"""
application = FastAPI(
title=settings.PROJECT_NAME,
description="Production-grade financial analytics platform API",
version=settings.VERSION,
openapi_url=f"{settings.API_V1_STR}/openapi.json",
docs_url=f"{settings.API_V1_STR}/docs",
redoc_url=f"{settings.API_V1_STR}/redoc",
lifespan=lifespan,
)
# CORS Middleware
# Explicitly allow Authorization header for JWT Bearer tokens
application.add_middleware(
CORSMiddleware,
allow_origins=settings.cors_origins_list,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"], # Includes Authorization header
expose_headers=["X-Process-Time"], # Expose custom headers
)
# Gzip compression for responses
application.add_middleware(GZipMiddleware, minimum_size=1000)
# Request timing middleware
@application.middleware("http")
async def add_process_time_header(request: Request, call_next):
"""Add X-Process-Time header to all responses."""
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
response.headers["X-Process-Time"] = str(process_time)
return response
# Include API routers
application.include_router(
api_router,
prefix=settings.API_V1_STR,
)
# Root endpoint
@application.get("/")
async def root():
"""Root endpoint - API information."""
return {
"name": settings.PROJECT_NAME,
"version": settings.VERSION,
"docs": f"{settings.API_V1_STR}/docs",
"health": f"{settings.API_V1_STR}/health",
}
# Global exception handlers
@application.exception_handler(Exception)
async def global_exception_handler(request: Request, exc: Exception):
"""Handle uncaught exceptions."""
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content={
"error": "Internal server error",
"message": str(exc) if settings.DEBUG else "An unexpected error occurred",
},
)
return application
# Create the application instance
app = create_application()