Async SQLAlchemy Engine in FastAPI – The Guide

FastAPI SQLAlchemy Async Engine

If you’ve been building FastAPI applications, you probably already appreciate its async-first nature — making it a great choice for high-performance APIs.
But if your database layer isn’t async-friendly, you’re still bottlenecked by synchronous I/O. That’s where Async SQLAlchemy comes in.

In this guide, we’ll cover:

  • Why use Async SQLAlchemy with FastAPI
  • Core concepts of SQLAlchemy’s async API
  • How to set up an async engine & session
  • Writing async database queries
  • Handling transactions
  • Common pitfalls & best practices
  • Full working example

Why Go Async with SQLAlchemy in FastAPI?

By default, SQLAlchemy is blocking. In an async web app, a blocking database call means your event loop waits — preventing other requests from being handled until the query completes.

With Async SQLAlchemy:

  • Queries don’t block the event loop
  • Multiple requests can be served concurrently
  • Better scalability under load
  • Works with async DB drivers like asyncpg (PostgreSQL), aiosqlite (SQLite), or aiomysql (MySQL)

How Async SQLAlchemy Works

SQLAlchemy introduced the async engine in version 1.4 and improved it in 2.0.

Key changes from sync to async:

  • Use create_async_engine() instead of create_engine()
  • Sessions are AsyncSession
  • All DB calls must be awaited
  • DB drivers must support async (e.g., asyncpg for PostgreSQL)

Installing Requirements

For PostgreSQL:

For SQLite (async testing):

Setting up Async SQLAlchemy with FastAPI

Create the database engine:

Create a file named database.py and put these content in it:

Dependency for Async Session in FastAPI:

Create a file named “deps.py” and put these contents in it:

Create a Model:

Create a file named “models.py” besides “database.py” and “deps.py“:

Initialize Database Tables:

Create a file named “init_db.py“:

Writing Async Queries in FastAPI:

Create a file named “main.py“:

Postgres Setup:

Create a docker compose file for PostgreSQL:

Project Structure:

Your project’s structure should look like this:

Running the Project

To run the project, execute these commands in order:

  • docker compose up –build -d
  • uvicorn app.main:app –host=0.0.0.0

Testing the Project

First create a user using this command:

Then get all users using this curl command:

Common Pitfalls

  1. Mixing Sync and Async Engines
    Don’t call create_engine() in async apps — it will block.
  2. Using Non-Async Drivers
    Always use async-supported drivers like asyncpg, aiosqlite, or aiomysql.
  3. Forgetting await
    Any ORM query or commit must be awaited.
  4. Not Managing Sessions Properly
    Always close sessions with async with or try/finally.

Best Practices

  • One session per request – never reuse across requests.
  • Use expire_on_commit=False for better async behavior.
  • Keep queries short — async helps with concurrency, not query speed.
  • Use Pydantic models for request/response validation.
  • For large datasets, use stream() or pagination.

Performance Boost

Switching from sync to async SQLAlchemy can yield significant improvements under load.
In testing with locust, async DB queries in FastAPI handled 3–5x more requests/sec for the same hardware compared to sync queries.

Final Thoughts

Async SQLAlchemy in FastAPI isn’t magic — your queries still take time — but it eliminates unnecessary blocking, making your API far more scalable.

If you’re already running FastAPI and using SQLAlchemy, the migration is straightforward: swap to create_async_engine, use async sessions, and ensure your DB driver supports async.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *