Open once, close once
FastAPI gives you a lifespan context manager that runs at startup and shutdown. The idiomatic pattern: open the database connection there and stash it on app.state, then expose it to routes via dependency injection.
This avoids the two pitfalls people hit:
- Opening a new connection per request — wastes file handles and prevents in-memory caching.
- Opening at module import time — runs before the event loop exists; aiosqlite needs the loop running.
Self-reference: Pippa's
backend/main.py uses exactly this lifespan pattern: open the SQLite store once at startup, run migrations, attach to app.state.store, and close cleanly at shutdown. Routes pull the store via Depends.