template updates

This commit is contained in:
John Lancaster
2026-08-06 23:17:50 -05:00
parent 0dc06f72ca
commit 7ac90d29dd
8 changed files with 352 additions and 295 deletions
+14 -32
View File
@@ -1,9 +1,9 @@
---
name: async-fastapi-sqlmodel
description: 'Explain and apply async database principles for FastAPI, SQLAlchemy 2.x, and SQLModel. Use when: learning or reviewing AsyncEngine and AsyncSession lifecycles, FastAPI lifespan and yield dependencies, transaction boundaries, concurrency safety, implicit ORM I/O, AsyncExitStack, pooling, testing, or SQLModel integration.'
description: 'Explain and apply async database principles for FastAPI, SQLAlchemy 2.x, and SQLModel. Use when: learning or reviewing cached AsyncEngine and session-factory lifecycles, AsyncSession scopes and injection, FastAPI lifespan and yield dependencies, transaction boundaries, concurrency safety, implicit ORM I/O, pooling, testing, or SQLModel integration.'
x-personal-mcp:
id: async-fastapi-sqlmodel
version: 1.1.0
version: 1.2.0
tags:
- fastapi
- sqlalchemy
@@ -24,6 +24,8 @@ Use this skill to explain how an async database layer works, why the recommended
Primary targets: PostgreSQL with asyncpg and SQLite with aiosqlite.
Engine and session mechanics mirror the [`nicegui-db` template repository](https://forgejo.john-stream.com/john/nicegui-db). Treat that template as the implementation baseline, then explain the rationale, lifecycle constraints, and tradeoffs behind its cached engines, session factories, context managers, dependency wiring, and `with_session` decorator. Source-specific claims in the references link to the reviewed template commit so behavior remains auditable as the template evolves.
## When to Use
- Explain an async engine, session factory, session, connection, or transaction.
@@ -51,7 +53,7 @@ Keep three ownership scopes distinct:
| Scope | Object | Purpose | Typical owner |
|---|---|---|---|
| Application process | `AsyncEngine` and `async_sessionmaker` | Dialect, connection pool, and repeatable session configuration | FastAPI lifespan |
| Application process | Cached `AsyncEngine` and lifespan-owned `async_sessionmaker` | Dialect, connection pool, schema initialization, and repeatable session configuration | FastAPI lifespan |
| Request or concurrent task | `AsyncSession` | Mutable ORM identity map and transactional state | A `yield` dependency or explicit unit of work |
| Atomic operation | `SessionTransaction` | Commit all changes together or roll them back together | Service or use-case boundary |
@@ -61,16 +63,16 @@ The engine is a long-lived factory and pool, not a single database connection. T
### Match lifetime to ownership
- Create one `AsyncEngine` per process and database configuration in the normal case.
- Dispose it explicitly in an awaitable shutdown path; garbage collection cannot reliably await async driver cleanup.
- Configure `async_sessionmaker` once inside the engine lifecycle and call it to create short-lived sessions.
- Resolve one cached `AsyncEngine` per database URL during the active application lifecycle.
- Enter one owning engine scope per URL; initialize registered SQLModel metadata by default, then dispose the engine and clear cached resolution on exit.
- Configure the application `async_sessionmaker` inside the engine lifecycle; use the template's cached factory resolver only for standalone helpers that cannot receive the application factory.
- Close each session deterministically with `async with` or a FastAPI dependency that yields once.
See [engine lifecycle](references/engine.md) and [session management](references/session.md).
### Isolate mutable session state
An `AsyncSession` represents one stateful transaction in progress. Never use one session in multiple concurrent tasks, including branches of `asyncio.gather()`. Give each task its own session and pass sessions explicitly rather than relying on mutable scoped globals.
An `AsyncSession` represents one stateful transaction in progress. Never use one session in multiple concurrent tasks, including branches of `asyncio.gather()`. Give each task its own session. Template-style `@with_session` functions inject one only when the `session` argument is omitted; a supplied session remains caller-owned, and explicit `None` is forwarded unchanged.
See [session management](references/session.md).
@@ -94,7 +96,7 @@ FastAPI lifespan owns resources shared by many requests. A dependency with one `
Use `AsyncExitStack` when lifespan acquires a variable, conditional, or mixed collection of context-managed resources. It records cleanup as resources are acquired and unwinds callbacks in reverse order. A single engine should use the direct engine context manager; `AsyncExitStack` is a composition tool, not a requirement.
See [engine lifecycle](references/engine.md).
See [FastAPI database integration](references/fastapi.md).
### Use SQLModel as the primary modeling layer
@@ -125,8 +127,8 @@ See [database testing and fixture data](references/testing.md).
| Engine lifecycle and ownership | [Engine lifecycle reference](references/engine.md) |
| Session factory and scope | [Session management reference](references/session.md) |
| Transaction boundaries | [Transaction boundaries reference](references/transactions.md) |
| Lifespan composition | [Engine lifecycle reference](references/engine.md) |
| Dependency injection | [Session management reference](references/session.md) |
| FastAPI lifespan composition | [FastAPI integration reference](references/fastapi.md) |
| FastAPI dependency injection | [FastAPI integration reference](references/fastapi.md) |
| Implicit I/O control in ORM | [Implicit I/O reference](references/implicit_io.md) |
| Observability and resilience | [Observability reference](references/observability.md) |
| SQLModel-first modeling | [SQLModel integration reference](references/sqlmodel.md) |
@@ -135,29 +137,9 @@ See [database testing and fixture data](references/testing.md).
## Canonical Composition Pattern
This example shows the ownership boundaries. Adapt state storage and dependency wiring to the application's conventions.
The framework-independent primitives live in [engine lifecycle](references/engine.md), [session management](references/session.md), and [transaction boundaries](references/transactions.md). Their canonical FastAPI adaptation, including lifespan state and `Annotated` dependencies, lives in [FastAPI database integration](references/fastapi.md).
```python
from collections.abc import AsyncGenerator
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request
from sqlmodel.ext.asyncio.session import AsyncSession
@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None]:
async with engine_scope(settings.database_url) as engine:
app.state.session_factory = create_session_factory(engine)
yield
async def get_session(request: Request) -> AsyncGenerator[AsyncSession]:
async with request.app.state.session_factory() as session:
yield session
```
`engine_scope()` and `create_session_factory()` are defined in the engine and session references. For background work that outlives a request, inject the shared factory and create a new session inside that task instead of retaining the request's session.
For background work that outlives a request, inject the shared factory and create a new session inside that task instead of retaining the request's session.
## Explanation Procedure