asgi-sqlalchemy
ASGI Middleware that manages the lifespan of a database engine and a corresponding session, featuring no global state, and automatic rollbacks on unhandled exceptions. Includes FastAPI and Starlette integrations.
I wrote about my motivations for this library in-depth here, but the short version is that using the ASGI lifespan protocol, we can avoid the use of global state, making database access more predictable and easier to test/mock.
Installation:
uv add asgi-sqlalchemy
Usage:
FastAPI:
from contextlib import AsyncContextManager
from collections.abc import AsyncGenerator
from typing_extensions import TypedDict
from fastapi import FastAPI
from asgi_sqlalchemy import DatabaseContext, SessionMiddleware
from asgi_sqlalchemy.fastapi import SessionDependency
class AppState(TypedDict):
db: DatabaseContext
async def lifespan() -> AsyncGenerator[AppState]:
async with DatabaseContext(...) as db:
yield {"db": db}
app = FastAPI()
app.add_middleware(SessionMiddleware)
@app.get("/db")
async def handler(session: SessionDependency) -> str:
# do something with your async session!
Starlette:
from contextlib import AsyncContextManager
from collections.abc import AsyncGenerator
from typing_extensions import TypedDict
from starlette.applications import Starlette
from starlette.requests import Request
from starlette.responses import JSONResponse
from starlette.routing import Route
from asgi_sqlalchemy import DatabaseContext, SessionMiddleware
from asgi_sqlalchemy.starlette import get_session
class AppState(TypedDict):
db: DatabaseContext
async def lifespan() -> AsyncGenerator[AppState]:
async with DatabaseContext(...) as db:
yield {"db": db}
async def handler(request: Request) -> JSONResponse:
session = await get_session(request)
# do something with your async session!
app = Starlette(routes=[Route("/db", handler)], lifespan=lifespan)
app.add_middleware(SessionMiddleware)
Tests:
This library was explicitly designed to be easy to test without dependency overrides.
- For synchronous tests, use FastAPI's
TestClient. - For asynchronous tests, use HTTPX
AsyncClientwithASGITransport, and wrap the app withLifespanManagerso startup/shutdown events run. See the FastAPI docs: Async Tests – In Detail. - To “mock” your database, provide a custom lifespan in tests that yields the database object you want (real DB or test double). The middleware will inject a fresh session per request, no dependency overrides needed.
See complete examples in tests/test_fastapi.py and tests/test_starlette.py inside the tests/ folder.
Metadata
Release files for asgi-sqlalchemy 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| asgi_sqlalchemy-0.2.0.tar.gz | 69.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| asgi_sqlalchemy-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 75.6 kB
Release files / asgi_sqlalchemy-0.2.0.tar.gz
| Download URL | asgi_sqlalchemy-0.2.0.tar.gz |
|---|---|
| Size | 69.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
78ba8e5ea9cb5cb12142860a7af18da5c827fb26afcdf1dfeef4ac5a19d61bf5
|
|
BLAKE2b-256 checksum How to use checksums |
a9e9573c2ddfb2a2e163bf19a3cccd29572677fcf16c45ea31c1026a064c732b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.8.15
|
Release files / asgi_sqlalchemy-0.2.0-py3-none-any.whl
| Download URL | asgi_sqlalchemy-0.2.0-py3-none-any.whl |
|---|---|
| Size | 6.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ab3cb98873a16d46edf169ddc19988aa5a011ae539779eef4a45d334b8492bd3
|
|
BLAKE2b-256 checksum How to use checksums |
67f500b814c1ab636c175b301f1005f7d2bd873dfca14e699e4bf6eef3499f14
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.8.15
|