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 defwhen you callawaitinside. Usedeffor 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 type | Use when |
|---|---|
def | blocking I/O (sync ORM, requests, file I/O) |
async def | awaitable I/O (asyncpg, httpx.AsyncClient, aiofiles) |
async def | CPU-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
| Mistake | Fix |
|---|---|
await inside def route | change to async def |
Blocking call (time.sleep, requests.get) in async def | use asyncio.sleep, httpx.AsyncClient, or anyio.to_thread |
| Creating a new event loop inside a route | never; use asyncio.get_event_loop() |
Sync SQLAlchemy in async def without threadpool | use anyio.to_thread.run_sync() or switch to async ORM |
Forgetting await on a coroutine | linters catch this; FastAPI will also warn |