Asynchronous Programming in FastAPI What You Need to Know

✅ 1. Introduction

FastAPI is one of the fastest-growing Python web frameworks, and one of its standout features is first-class support for asynchronous programming. But what does "async" really mean in practice? When should you use it, and when should you avoid it?

In this post, we'll break down the essentials of asynchronous programming in FastAPI. Whether you're new to async/await or wondering how it affects your API performance, this guide will help you understand the concepts and write better, faster web applications.

✅ 2. What is Asynchronous Programming?

In traditional (synchronous) Python code, each operation happens one after another. If your code needs to wait for something—like a file to load or a network response—it blocks the entire thread until that operation finishes.
FastAPI Async Request Flow

Asynchronous programming changes that. Instead of waiting, async code suspends execution while it waits and lets other tasks run in the meantime. This is especially useful for I/O-bound tasks like:

  • Making HTTP requests
  • Reading/writing files
  • Talking to a database

With Python’s async def and await syntax, you can define coroutines that efficiently handle many I/O operations concurrently—without creating new threads or processes.

✅ 3. Async vs Sync in Python: A Quick Primer

Understanding the difference between synchronous and asynchronous code in Python is essential before diving into FastAPI's async capabilities.

In synchronous code, everything runs in sequence. If a function takes time (e.g., fetching a webpage), the entire program pauses until that task finishes:

1
2
3
4
5
6
7
8
import requests

def get_data():
response = requests.get("https://httpbin.org/delay/3")
return response.json()

# This call blocks until the response is returned
data = get_data()

In contrast, asynchronous code uses async def to define a coroutine, and await to pause the coroutine until a result is ready—without blocking other tasks:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import httpx
import asyncio
from datetime import datetime

async def get_data():
print(f"[{datetime.now()}] get_data start")
async with httpx.AsyncClient() as client:
response = await client.get("https://httpbin.org/delay/3")
print(f"[{datetime.now()}] get_data end")
return response.json()

async def quick_task():
print(f"[{datetime.now()}] quick_task start")
await asyncio.sleep(1)
print(f"[{datetime.now()}] quick_task end")

async def main():
task1 = asyncio.create_task(get_data())
task2 = asyncio.create_task(quick_task())

await asyncio.gather(task1, task2)

if __name__ == "__main__":
asyncio.run(main())

So,You can see that get_data does not block the quick_task function.

The key differences:

Feature Synchronous (def) Asynchronous (async def)
Blocking Yes No (when using await)
Runs concurrently No Yes (with event loop)
Suitable for CPU-bound tasks I/O-bound tasks

Note: Just writing async def doesn't make your function run in parallel—you still need to await asynchronous calls inside it.


✅ 4. Why Async Matters in FastAPI

FastAPI is built on top of Starlette, which is an ASGI (Asynchronous Server Gateway Interface) framework. This gives FastAPI native support for asynchronous endpoints using Python’s async/await syntax.

This means FastAPI can handle many I/O-bound operations—like reading from a database, calling external APIs, or streaming data—without blocking the main thread. This leads to higher performance under load, especially in real-time and high-concurrency environments.

Here's what an async route looks like in FastAPI:

1
2
3
4
5
6
7
8
9
10
from fastapi import FastAPI
import httpx

app = FastAPI()

@app.get("/quote")
async def fetch_quote():
async with httpx.AsyncClient() as client:
response = await client.get("https://httpbin.org/delay/3")
return response.json()

Because this endpoint is asynchronous, FastAPI can continue processing other requests while it waits for the API response—improving throughput and responsiveness.

🧠 Good to know: You can still define sync endpoints using normal def, and FastAPI will run them in a thread pool—but async functions are more efficient for I/O-bound operations.

✅ 5. Defining async endpoints in FastAPI

FastAPI makes it incredibly simple to define asynchronous endpoints. All you need to do is declare your route handler with async def, and use await inside it for any I/O-bound operations.

Here's a minimal example:

1
2
3
4
5
6
7
from fastapi import FastAPI

app = FastAPI()

@app.get("/ping")
async def ping():
return {"message": "pong"}

In this example, the /ping route is defined using async def, but since it doesn't do any real I/O, it runs like a normal function. You only gain performance benefits when you use await with async-compatible libraries.

Let's compare an async and sync version of the same endpoint
Async version (recommended for I/O-bound tasks):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import httpx
import requests

@app.get("/async-quote")
async def async_quote():
async with httpx.AsyncClient() as client:
response = await client.get("https://httpbin.org/delay/3")
return response.json()

# Sync version (also supported):
@app.get("/sync-quote")
def sync_quote():
response = requests.get("https://httpbin.org/delay/3")
return response.json()

🔥 FastAPI supports both def and async def endpoints. However, only async def endpoints can fully take advantage of FastAPI's non-blocking ASGI stack for high concurrency.
Internally, FastAPI will run def-based functions in a thread pool (via run_in_threadpool), which adds some overhead and doesn't scale as well for thousands of concurrent connections.

✅ 6. await in action: I/O-bound examples (with async database)

One of the most common and powerful uses of await in FastAPI is when dealing with I/O-bound operations like querying a database. In synchronous code, such operations block the main thread. But with asynchronous drivers, FastAPI can handle many requests concurrently, even if some are waiting for database responses.

Here's a practical example using FastAPI with SQLModel and an async engine powered by SQLAlchemy 1.4+ and asyncpg.

The code for this article is located in the GitHub repository named 04_fastapi_async.
https://github.com/gzthss/fastapi_tutorial.git

🔧 Setup: Install requirements

1
pip install sqlmodel[asyncio] asyncpg

Let's walk through a practical example using SQLModel (an async-compatible ORM from FastAPI’s creator) and Postgresql.

1
2
3
4
5
6
7
8
9
10
# models.py
from sqlmodel import SQLModel, Field
from typing import Optional

class Item(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
description: Optional[str] = None


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# database.py
from sqlmodel import SQLModel
from sqlmodel.ext.asyncio.session import AsyncSession
from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker

database_url = "postgresql+asyncpg://postgres:admin123@192.168.1.11:5432/postgres"

engine = create_async_engine(database_url, echo=True)
async_session = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)

async def get_session():
async with async_session() as session:
yield session

async def init_db():
async with engine.begin() as conn:
await conn.run_sync(SQLModel.metadata.create_all)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# main.py
from fastapi import FastAPI, HTTPException, Depends
from sqlmodel import select
import uvicorn
from database import init_db, engine, get_session
from models import Item
from sqlmodel.ext.asyncio.session import AsyncSession

app = FastAPI()

@app.on_event("startup")
async def startup():
await init_db()

@app.get("/items/{item_id}")
async def read_item(item_id: int, session: AsyncSession = Depends(get_session)):
query = select(Item).where(Item.id == item_id)
result = await session.exec(query)
item = result.one_or_none()
if item is None:
raise HTTPException(status_code=404, detail="Item not found")
return item

if __name__ == "__main__":
uvicorn.run(app, host="127.0.0.1", port=8000)

The image below shows the result of the API call.

💗💗💗I hope this article helps you better understand asynchronous programming in FastAPI and improves your API performance in real-world projects. If you found it helpful, feel free to share it with others!


Asynchronous Programming in FastAPI What You Need to Know
https://gzthss.github.io/2025/05/21/Asynchronous-Programming-in-FastAPI-What-You-Need-to-Know/
Author
GZTHSS
Posted on
May 21, 2025
Licensed under