[{"data":1,"prerenderedAt":88},["ShallowReactive",2],{"qa-\u002Ffastapi\u002Fdatabase\u002Fasync-db":3},{"page":4,"siblings":78,"blog":69},{"id":5,"title":6,"body":7,"description":11,"difficulty":14,"extension":15,"framework":16,"frameworkSlug":17,"meta":18,"navigation":19,"order":12,"path":20,"questions":21,"questionsCount":68,"related":69,"seo":70,"seoDescription":71,"stem":72,"subtopic":73,"topic":74,"topicSlug":75,"updated":76,"__hash__":77},"qa\u002Ffastapi\u002Fdatabase\u002Fasync-db.md","Async Db",{"type":8,"value":9,"toc":10},"minimark",[],{"title":11,"searchDepth":12,"depth":12,"links":13},"",2,[],"hard","md","FastAPI","fastapi",{},true,"\u002Ffastapi\u002Fdatabase\u002Fasync-db",[22,27,31,35,39,43,47,52,56,60,64],{"id":23,"difficulty":24,"q":25,"a":26},"why-async-db","medium","Why use async database drivers with FastAPI instead of sync SQLAlchemy?","Synchronous DB drivers block the OS thread during the query. In a FastAPI\n`async def` handler, this freezes the **entire event loop** — no other requests\ncan be processed until the query returns.\n\n```python\n# BAD — sync driver inside async handler blocks the event loop\n@app.get(\"\u002Fusers\")\nasync def list_users(db: Session = Depends(get_db)):\n    return db.query(User).all()   # blocks event loop for entire query duration\n\n# GOOD — async driver yields control during the query\n@app.get(\"\u002Fusers\")\nasync def list_users(db: AsyncSession = Depends(get_async_db)):\n    result = await db.execute(select(User))\n    return result.scalars().all()\n```\n\nAsync drivers (`asyncpg`, `aiosqlite`, `aiomysql`) suspend the coroutine during\nI\u002FO, letting the event loop handle other requests.\n\nRule of thumb: always use async DB drivers in `async def` FastAPI handlers;\nuse sync drivers only with `def` handlers (FastAPI runs them in a thread pool).\n",{"id":28,"difficulty":24,"q":29,"a":30},"async-engine-setup","How do you set up an async SQLAlchemy engine and session for FastAPI?","```python\nfrom sqlalchemy.ext.asyncio import (\n    create_async_engine, AsyncSession, async_sessionmaker\n)\nfrom sqlalchemy.orm import DeclarativeBase\n\n# asyncpg driver for PostgreSQL\nDATABASE_URL = \"postgresql+asyncpg:\u002F\u002Fuser:pass@localhost\u002Fmydb\"\n\nengine = create_async_engine(DATABASE_URL, pool_size=10, echo=False)\nasync_session_factory = async_sessionmaker(\n    engine, class_=AsyncSession, expire_on_commit=False\n)\n\nclass Base(DeclarativeBase):\n    pass\n```\n\n`expire_on_commit=False` prevents SQLAlchemy from expiring (lazily reloading)\nattributes after a commit — essential in async code where lazy loading would\ntrigger a new I\u002FO call without being awaited.\n\nRule of thumb: always set `expire_on_commit=False` for async sessions — expired\nattributes in async context cause `MissingGreenlet` or `DetachedInstanceError`.\n",{"id":32,"difficulty":24,"q":33,"a":34},"async-get-db","How do you write the `get_async_db` dependency for async SQLAlchemy?","```python\nfrom sqlalchemy.ext.asyncio import AsyncSession\nfrom fastapi import Depends\n\nasync def get_async_db():\n    async with async_session_factory() as session:\n        yield session\n    # async context manager commits on clean exit, rolls back on exception\n\n# Or explicit try\u002Fexcept for fine-grained control:\nasync def get_async_db():\n    session = AsyncSession(engine)\n    try:\n        yield session\n        await session.commit()\n    except Exception:\n        await session.rollback()\n        raise\n    finally:\n        await session.close()\n\n@app.get(\"\u002Fusers\")\nasync def list_users(db: AsyncSession = Depends(get_async_db)):\n    result = await db.execute(select(User))\n    return result.scalars().all()\n```\n\nRule of thumb: prefer `async with async_session_factory() as session` — it\nhandles commit\u002Frollback automatically and is harder to get wrong.\n",{"id":36,"difficulty":24,"q":37,"a":38},"async-query-patterns","What are the key async query patterns with SQLAlchemy async?","```python\nfrom sqlalchemy import select, func\n\n# Single object by primary key\nuser = await db.get(User, user_id)\n\n# Filtered query\nresult = await db.execute(select(User).where(User.is_active == True))\nusers = result.scalars().all()\n\n# Single result\nresult = await db.execute(select(User).where(User.email == email))\nuser = result.scalar_one_or_none()   # None if not found, raises if multiple\n\n# Count\nresult = await db.execute(select(func.count()).select_from(User))\ncount = result.scalar()\n\n# Insert\ndb.add(User(name=\"Alice\", email=\"alice@example.com\"))\nawait db.commit()\n\n# Update\nawait db.execute(update(User).where(User.id == id).values(name=\"Bob\"))\nawait db.commit()\n```\n\nRule of thumb: always `await db.execute(stmt)` for queries; always `await db.commit()`\nafter writes — forgetting `await` on async methods causes silent failures.\n",{"id":40,"difficulty":14,"q":41,"a":42},"async-relationships","How do you load SQLAlchemy relationships in async code?","Lazy loading is **not available** in async SQLAlchemy — accessing an unloaded\nrelationship raises `MissingGreenlet`. You must eager-load:\n\n```python\nfrom sqlalchemy.orm import selectinload, joinedload\n\n# selectinload — runs a second SELECT IN query\nresult = await db.execute(\n    select(User).options(selectinload(User.posts)).where(User.id == user_id)\n)\nuser = result.scalar_one()\n# user.posts is now loaded — no extra query needed\n\n# joinedload — single JOIN query\nresult = await db.execute(\n    select(Post).options(joinedload(Post.author))\n)\nposts = result.unique().scalars().all()\n```\n\n`result.unique()` is needed after `joinedload` to de-duplicate rows from the JOIN.\n\nRule of thumb: always use `selectinload` (one-to-many) or `joinedload` (many-to-one)\nin async; lazy loading raises an error rather than silently blocking.\n",{"id":44,"difficulty":24,"q":45,"a":46},"asyncpg-vs-psycopg3","What async PostgreSQL drivers work with SQLAlchemy and FastAPI?","| Driver | URL prefix | Notes |\n|--------|-----------|-------|\n| `asyncpg` | `postgresql+asyncpg:\u002F\u002F` | Fastest, pure async; most popular |\n| `psycopg3` (psycopg) | `postgresql+psycopg:\u002F\u002F` | psycopg 3.x; sync + async |\n| `aiopg` | `postgresql+aiopg:\u002F\u002F` | Older, based on psycopg2 |\n\n```bash\npip install asyncpg            # for asyncpg\npip install psycopg[binary]   # for psycopg3\n```\n\n```python\n# asyncpg\nengine = create_async_engine(\"postgresql+asyncpg:\u002F\u002Fuser:pass@host\u002Fdb\")\n\n# psycopg3\nengine = create_async_engine(\"postgresql+psycopg:\u002F\u002Fuser:pass@host\u002Fdb\")\n```\n\n`asyncpg` is the most battle-tested; `psycopg3` supports binary protocol and\nis gaining adoption.\n\nRule of thumb: use `asyncpg` for new projects — it's the most performant and\nhas the widest ecosystem support.\n",{"id":48,"difficulty":49,"q":50,"a":51},"aiosqlite","easy","How do you use SQLite with async FastAPI (e.g., for testing)?","Use the `aiosqlite` driver:\n\n```bash\npip install aiosqlite\n```\n\n```python\nfrom sqlalchemy.ext.asyncio import create_async_engine\n\n# In-memory for tests\nengine = create_async_engine(\"sqlite+aiosqlite:\u002F\u002F\u002F:memory:\", echo=True)\n\n# File-based\nengine = create_async_engine(\"sqlite+aiosqlite:\u002F\u002F\u002F.\u002Ftest.db\")\n```\n\nFor tests, create tables in the test fixture:\n```python\n@pytest.fixture\nasync def db_engine():\n    engine = create_async_engine(\"sqlite+aiosqlite:\u002F\u002F\u002F:memory:\")\n    async with engine.begin() as conn:\n        await conn.run_sync(Base.metadata.create_all)\n    yield engine\n    await engine.dispose()\n```\n\nRule of thumb: use `sqlite+aiosqlite:\u002F\u002F\u002F:memory:` in tests for fast, isolated\nDB fixtures — no cleanup needed since the DB disappears when the engine is disposed.\n",{"id":53,"difficulty":14,"q":54,"a":55},"async-session-scoping","What is `async_scoped_session` and when would you use it?","`async_scoped_session` binds sessions to an `asyncio.Task` (coroutine) context\nrather than a thread. It's the async equivalent of `scoped_session`.\n\n```python\nimport asyncio\nfrom sqlalchemy.ext.asyncio import async_scoped_session\n\nAsyncScopedSession = async_scoped_session(\n    async_session_factory,\n    scopefunc=asyncio.current_task,\n)\n```\n\nWith `scopefunc=asyncio.current_task`, each asyncio Task gets its own session.\nCall `AsyncScopedSession.remove()` to close and remove the session at the end\nof a task.\n\nIn FastAPI, the per-request `get_async_db` yield dep is simpler and preferred.\n`async_scoped_session` is useful in background workers or batch scripts where\nyou don't have a FastAPI dependency container.\n\nRule of thumb: use `async_scoped_session` in standalone async scripts; use the\nyield dep pattern in FastAPI request handlers.\n",{"id":57,"difficulty":14,"q":58,"a":59},"run-sync-in-async-db","How do you run synchronous SQLAlchemy operations inside an async context?","Use `connection.run_sync()` to execute sync SQLAlchemy operations on the async\nconnection without blocking the event loop:\n\n```python\nasync with engine.begin() as conn:\n    # run_sync calls sync code inside the driver's thread pool\n    await conn.run_sync(Base.metadata.create_all)\n    await conn.run_sync(Base.metadata.drop_all)\n```\n\nFor executing sync SQLAlchemy session operations (bulk inserts with the legacy\nAPI):\n```python\nasync with AsyncSession(engine) as session:\n    def _sync_bulk_insert(sync_session):\n        sync_session.bulk_insert_mappings(User, data)\n    await session.run_sync(_sync_bulk_insert)\n```\n\nRule of thumb: use `run_sync` only for operations that have no async equivalent\n(Alembic migrations, legacy bulk operations); prefer the async API everywhere else.\n",{"id":61,"difficulty":14,"q":62,"a":63},"connection-pool-async","How does the async SQLAlchemy connection pool behave with multiple Uvicorn workers?","Each Uvicorn worker process has **its own event loop and connection pool**. With\n4 workers and `pool_size=10`, you have 40 connections total to the database.\n\n`asyncpg` uses a native connection pool built on the event loop — connections\nare not thread-safe and must be used within the same event loop they were created on.\n\n```python\nengine = create_async_engine(\n    DATABASE_URL,\n    pool_size=5,         # connections per worker\n    max_overflow=5,      # burst pool per worker\n    pool_timeout=30,\n    pool_recycle=3600,   # re-connect periodically\n)\n```\n\nPre-fork (Gunicorn) models must create the engine **after** forking — creating\nit before the fork shares connection file descriptors across workers, causing\ncorruption.\n\nRule of thumb: create the async engine inside the `lifespan` function so each\nworker process creates its own after forking.\n",{"id":65,"difficulty":14,"q":66,"a":67},"transactions-async","How do you manage database transactions explicitly in async SQLAlchemy?","By default, `AsyncSession` runs in autobegin mode — a transaction starts\nautomatically on the first operation. Use `begin()` for explicit transactions:\n\n```python\nasync with AsyncSession(engine) as session:\n    async with session.begin():\n        # all operations here are in one transaction\n        session.add(User(name=\"Alice\"))\n        session.add(Order(user_id=1, total=99.99))\n        # commit happens automatically when the context manager exits\n\n# Or manually:\nsession = AsyncSession(engine)\ntry:\n    session.add(user)\n    session.add(order)\n    await session.commit()\nexcept Exception:\n    await session.rollback()\n    raise\nfinally:\n    await session.close()\n```\n\nRule of thumb: use `async with session.begin()` for atomic multi-step operations —\nit makes the transaction boundary explicit and ensures rollback on any exception.\n",11,null,{"description":11},"FastAPI async database interview questions — AsyncSession, async SQLAlchemy, asyncpg, aiosqlite and event loop compatibility.","fastapi\u002Fdatabase\u002Fasync-db","Async Database","Database Integration","database","2026-06-20","I90QMM52vWiPFlTjknINkakJ_qhvXcpmWK2zkfuOniA",[79,83,84],{"subtopic":80,"path":81,"order":82},"SQLAlchemy (Sync)","\u002Ffastapi\u002Fdatabase\u002Fsqlalchemy",1,{"subtopic":73,"path":20,"order":12},{"subtopic":85,"path":86,"order":87},"Alembic Migrations","\u002Ffastapi\u002Fdatabase\u002Fmigrations",3,1782244113413]