FastAPI Cheatsheet

Async

Use this FastAPI reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

sync vs async Route Functions

from fastapi import FastAPI

app = FastAPI()

# Sync — FastAPI runs this in a threadpool automatically
@app.get("/sync")
def sync_route():
    result = blocking_db_call()   # OK here — not blocking the event loop
    return result

# Async — runs on the event loop; NEVER block here
@app.get("/async")
async def async_route():
    result = await async_db_call()
    return result

Rule: Use async def when you call await inside. Use def for blocking I/O (SQLAlchemy sync, requests library, etc.) — FastAPI offloads it to a thread automatically. Mixing them wrong causes deadlocks or blocked event loops.

When to Use Each

Route typeUse when
defblocking I/O (sync ORM, requests, file I/O)
async defawaitable I/O (asyncpg, httpx.AsyncClient, aiofiles)
async defCPU-light logic (no I/O) — fine either way

await — Common Patterns

import httpx
import asyncio

@app.get("/external")
async def call_external():
    async with httpx.AsyncClient() as client:
        resp = await client.get("https://api.example.com/data")
        return resp.json()

@app.get("/parallel")
async def parallel():
    # Run two awaitable calls concurrently:
    result_a, result_b = await asyncio.gather(
        fetch_a(),
        fetch_b(),
    )
    return {"a": result_a, "b": result_b}

Async Database (asyncpg / SQLAlchemy async)

from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession
from sqlalchemy.orm import sessionmaker

engine = create_async_engine("postgresql+asyncpg://user:pass@host/db")
AsyncSessionLocal = sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)

async def get_db():
    async with AsyncSessionLocal() as session:
        yield session

@app.get("/users")
async def list_users(db: AsyncSession = Depends(get_db)):
    result = await db.execute(select(User))
    return result.scalars().all()

Async Dependencies

async def get_token_data(token: str = Depends(oauth2_scheme)):
    payload = await async_verify_token(token)
    return payload

@app.get("/me")
async def me(data = Depends(get_token_data)):
    return data

Running Blocking Code in async Context

import asyncio
from concurrent.futures import ThreadPoolExecutor

executor = ThreadPoolExecutor()

@app.get("/cpu-heavy")
async def cpu_heavy():
    loop = asyncio.get_event_loop()
    result = await loop.run_in_executor(executor, blocking_cpu_function, arg)
    return {"result": result}

# Shortcut with anyio:
import anyio

@app.get("/blocking")
async def blocking():
    result = await anyio.to_thread.run_sync(sync_blocking_func)
    return result

Async Context Managers in Routes

@app.get("/redis")
async def redis_route():
    async with aioredis.from_url("redis://localhost") as redis:
        value = await redis.get("key")
        return {"value": value}

Lifespan with Async Resources

from contextlib import asynccontextmanager
import httpx

http_client: httpx.AsyncClient | None = None

@asynccontextmanager
async def lifespan(app: FastAPI):
    global http_client
    http_client = httpx.AsyncClient()
    yield
    await http_client.aclose()

app = FastAPI(lifespan=lifespan)

@app.get("/fetch")
async def fetch(url: str):
    resp = await http_client.get(url)
    return resp.json()

asyncio.gather vs asyncio.TaskGroup

# asyncio.gather — cancel all on first error
async def route():
    a, b, c = await asyncio.gather(task_a(), task_b(), task_c())
    return [a, b, c]

# asyncio.TaskGroup (Python 3.11+) — structured concurrency
async def route():
    async with asyncio.TaskGroup() as tg:
        task_a = tg.create_task(fetch_a())
        task_b = tg.create_task(fetch_b())
    return [task_a.result(), task_b.result()]

Async File I/O

# pip install aiofiles
import aiofiles

@app.get("/read-file")
async def read_file():
    async with aiofiles.open("data.txt", "r") as f:
        content = await f.read()
    return {"content": content}

WebSocket (async required)

from fastapi import WebSocket

@app.websocket("/ws")
async def websocket_endpoint(ws: WebSocket):
    await ws.accept()
    while True:
        data = await ws.receive_text()
        await ws.send_text(f"Echo: {data}")

Streaming Responses (async generator)

from fastapi.responses import StreamingResponse

async def event_generator():
    for i in range(100):
        await asyncio.sleep(0.1)
        yield f"data: {i}\n\n"

@app.get("/sse")
async def sse():
    return StreamingResponse(event_generator(), media_type="text/event-stream")

Common Mistakes

MistakeFix
await inside def routechange to async def
Blocking call (time.sleep, requests.get) in async defuse asyncio.sleep, httpx.AsyncClient, or anyio.to_thread
Creating a new event loop inside a routenever; use asyncio.get_event_loop()
Sync SQLAlchemy in async def without threadpooluse anyio.to_thread.run_sync() or switch to async ORM
Forgetting await on a coroutinelinters catch this; FastAPI will also warn