fastapi updates

This commit is contained in:
John Lancaster
2026-07-30 01:28:39 -05:00
parent 3abafc4850
commit b6f109cf91
6 changed files with 183 additions and 11 deletions
+17 -2
View File
@@ -110,6 +110,14 @@ Pool sizing, overflow, recycle, pre-ping, isolation, statement timeouts, and hea
See [observability and resilience](references/observability.md).
### Test through the production seam
Keep the production engine and session-factory construction path intact in tests. Select a dedicated PostgreSQL, local SQLite, or in-memory SQLite URL at that seam, then override the request-session dependency only for the test lifetime. Use a test-scoped outer transaction with SAVEPOINT-backed session commits when application code calls `commit()`; it exercises normal transaction behavior while cleanup remains deterministic.
In-memory SQLite is suitable for serial tests. For multiple simultaneous sessions, use a named shared-cache SQLite URL or a temporary file, and retain PostgreSQL integration coverage for PostgreSQL-specific behavior.
See [database testing and fixture data](references/testing.md).
## Reference Map
| Concept | Reference |
@@ -123,6 +131,7 @@ See [observability and resilience](references/observability.md).
| Observability and resilience | [Observability reference](references/observability.md) |
| SQLModel-first modeling | [SQLModel integration reference](references/sqlmodel.md) |
| CRUD repository and standalone functions | [Basic CRUD reference](references/crud.md) |
| Test database selection and fixture data | [Database testing reference](references/testing.md) |
## Canonical Composition Pattern
@@ -133,7 +142,8 @@ from contextlib import AsyncExitStack, asynccontextmanager
from collections.abc import AsyncIterator
from fastapi import FastAPI
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker, create_async_engine
from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine
from sqlmodel.ext.asyncio.session import AsyncSession
@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncIterator[None]:
@@ -141,7 +151,11 @@ async def lifespan(app: FastAPI) -> AsyncIterator[None]:
engine = create_async_engine(settings.database_url)
stack.push_async_callback(engine.dispose)
session_factory = async_sessionmaker(engine, expire_on_commit=False)
session_factory = async_sessionmaker(
engine,
class_=AsyncSession,
expire_on_commit=False,
)
app.state.session_factory = session_factory
yield
@@ -173,6 +187,7 @@ When reviewing code, verify:
- Relationship and deferred-column access cannot surprise the event loop with implicit I/O.
- Pool and timeout settings are justified by deployment behavior.
- Tests exercise rollback, cleanup, concurrency, and lifespan behavior where relevant.
- Tests use a dedicated database target and preserve production session mechanics.
## Anti-Patterns to Flag