Mental model
SQLAlchemy 2.0 introduces native async support over AsyncIO drivers (asyncpg). Combining FastAPI dependency injection with AsyncSession guarantees clean connection acquisition and teardown on every request tick.
Theory
When running async route handlers (async def), executing blocking synchronous ORM calls (session.query()) blocks Python's single-threaded event loop. AsyncEngine combined with asyncpg offloads database socket I/O to non-blocking epoll events.
Alternatives and trade-offs
- Synchronous ORM (
psycopg2): Simple; blocks AsyncIO event loop unless wrapped inrun_in_executor. - SQLAlchemy 2.0 Async (
asyncpg): High concurrency non-blocking I/O; requires explicitawaiton all DB operations and lazy loading handling.
Failure modes and misconceptions
- Implicit Lazy Loading Crashes: Accessing un-loaded ORM relationships (
user.orders) in AsyncIO raisesMissingGreenleterror. Use explicit joined loads (options(joinedload(User.orders))). expire_on_commit=TrueOverhead: Default SQLAlchemy setting expires object attributes on commit, forcing secondary async DB fetches when accessing object attributes after commit. Always setexpire_on_commit=False.
Decision scenario
Always set expire_on_commit=False on async_sessionmaker and use explicit eager loading (joinedload/selectinload) to avoid MissingGreenlet errors in FastAPI async routes.
Learning outcomes
- Build non-blocking FastAPI database integration with SQLAlchemy 2.0 and
asyncpg. - Manage
AsyncSessionlifecycles safely using FastAPI dependency injection. - Prevent
MissingGreenleterrors using explicit eager loading strategies.
Trade-offs
SQLAlchemy 2.0 Async delivers massive concurrency under high HTTP load, but requires disciplined handling of relationship loading and explicit session scopes.