learn.chetana.fr

App factory, lifespan et injection de dépendances

13 min de lectureEssentiel

create_app() : l'assembleur

Le package api/ ne contient presque aucune logique : c'est l'assembleur. Sa fonction create_app() construit l'application en montant les routers de chaque module :

def create_app() -> FastAPI:
    app = FastAPI(lifespan=lifespan)
    app.add_middleware(CORSMiddleware, ...)
    app.add_middleware(AuthMiddleware)          # JWT → tenant_id (module 3)
    app.include_router(copilot_router,  prefix="/api/copilot")
    app.include_router(catalog_router,  prefix="/api/catalog")
    app.include_router(engine_router)           # /extract, /jobs/{id}
    return app

Chaque module expose son router.py (la couche HTTP) qui dĂ©lĂšgue Ă  ses services/ (le mĂ©tier). Trois couches, pas plus : router → service → shared.

lifespan : le cùblage au démarrage

Le lifespan (async context manager) remplace les vieux startup/shutdown events. C'est lĂ  que tout se branche :

@asynccontextmanager
async def lifespan(app: FastAPI):
    engine = create_async_engine(DATABASE_URL, pool_pre_ping=True)
    checkpointer = await setup_postgres_checkpointer()      # LangGraph, module 6
    app.state.copilot_service = CopilotService(
        catalog=LocalCatalogProvider(...),     # les Protocols du module 0,
        prices=LocalPriceProvider(...),        # cùblés ICI et nulle part ailleurs
    )
    reconciler = asyncio.create_task(job_reconcile_loop())  # boucles de fond
    yield                                                    # ← l'app tourne
    reconciler.cancel()                                      # arrĂȘt propre
    await engine.dispose()

L'injection du projet est volontairement simple : pas de conteneur DI magique — les dĂ©pendances vivent sur app.state, posĂ©es une fois au boot, lues par les handlers via request.app.state. Explicite, typĂ©, testable (en test on pose des fakes sur app.state).

Les middlewares : l'oignon

Chaque requĂȘte traverse, dans l'ordre : sĂ©curitĂ© (headers) → logs structurĂ©s (structlog, un JSON par requĂȘte) → mĂ©triques RED (rate/errors/duration, exposĂ©es pour Prometheus) → auth (valide le JWT, rĂ©sout le tenant_id, le pose sur request.state) → le router. Le tenant_id posĂ© ici est LA donnĂ©e que tout le reste consomme — module suivant.

đŸ§© Quiz1/3

OĂč les implĂ©mentations concrĂštes des Protocols sont-elles instanciĂ©es ?

🃏 Flashcards1/4