Lesson 6 / 26
Async Endpoints
Concurrency for I/O-bound work.
async def and await
Endpoints defined with async def run on the event loop and can await I/O (HTTP calls with httpx, async database drivers) without blocking other requests; asyncio.gather runs independent awaits concurrently. Never call blocking functions (time.sleep, requests.get, synchronous database drivers) inside async def: they freeze the loop. Plain def endpoints are run in a thread pool, which is fine for blocking libraries.
Three awaited calls running concurrently, run
I ran this with Python 3.12, FastAPI 0.142.2, Pydantic 2.13 and Starlette 1.7, calling the app through FastAPI's TestClient (no server needed). Three simulated 200 ms lookups awaited with asyncio.gather finish together in well under 0.5 seconds, and the total is 2,548.
import asyncio, time
from fastapi import FastAPI
from fastapi.testclient import TestClient
app = FastAPI()
async def fetch_price(item: str) -> float:
await asyncio.sleep(0.2) # pretend I/O: an HTTP call or DB query
return {"pen": 899.0, "ink": 150.0, "lamp": 1499.0}[item]
@app.get("/quote")
async def quote():
start = time.perf_counter()
prices = await asyncio.gather(*(fetch_price(i) for i in ["pen", "ink", "lamp"]))
return {"total": sum(prices), "concurrent": time.perf_counter() - start < 0.5}
print(TestClient(app).get("/quote").json())
Output:
{'total': 2548.0, 'concurrent': True}Match def or async def to your libraries
Use async def only with async libraries; with blocking libraries, plain def runs safely in a thread pool.
Quick check: What happens if you call time.sleep(2) inside an async def endpoint?
- FastAPI converts it to await
- Nothing, it is async
- It runs in parallel automatically
- It blocks the event loop and stalls other requests
Answer
It blocks the event loop and stalls other requests — Use await asyncio.sleep or async libraries.