FastAPI supports both async def and regular def for path operations. The choice matters for performance: use async def when you can await non-blocking I/O, and regular when your code calls blocking (synchronous) operations.
def@app.get("/async")
async def async_endpoint():
data = await fetch_from_api() # await non-blocking I/O — efficient
return data
@app.get("/sync")
def sync_endpoint():
data = blocking_db_call() # ordinary blocking code
return data
async def → runs on the main event loop. Efficient ONLY if you await non-blocking calls.
⚠️ A BLOCKING call inside async def blocks the WHOLE event loop → kills concurrency!
def → FastAPI runs it in a THREAD POOL, so blocking code doesn't block the event loop.
Safe for synchronous/blocking libraries.
# ✅ async def — when you can await async libraries (httpx, async DB drivers)
async def get_user():
async with httpx.AsyncClient() as c:
return await c.get(url)
# ✅ plain def — when using SYNCHRONOUS/blocking libraries (requests, sync ORM)
def get_user():
return requests.get(url).json() # blocking → FastAPI runs it in a thread
# ❌ THE DANGEROUS MISTAKE — blocking call inside async def
async def bad():
return requests.get(url).json() # blocks the event loop! Use `def` or an async client
The critical rule: never put blocking calls in async def — it blocks the event loop and destroys concurrency. Either use an async library with await, or use a plain def (which FastAPI safely runs in a thread pool).
Choosing correctly between async def and def directly affects your API's performance and concurrency, and getting it wrong is a common, serious mistake.
The key insight is that async def is only beneficial when you await non-blocking operations — putting a blocking call (like the synchronous requests library or a blocking database driver) inside an async def blocks the entire event loop, freezing all concurrent requests and defeating the purpose of async.
Conversely, FastAPI cleverly runs plain def endpoints in a thread pool, so synchronous/blocking code is safe there.
Understanding this rule — use async def with truly async libraries, use plain def for blocking code, and never mix blocking calls into async def — is essential for writing performant FastAPI endpoints and avoiding the subtle but devastating mistake of accidentally serializing all requests behind a blocked event loop.