Asynchronous programming with FastAPI (async/await)
Asynchronous Programming with FastAPI: Understanding async and await in Python APIs
Asynchronous Programming with FastAPI (async/await)
Modern APIs often need to handle many requests at the same time. And that’s where asynchronous programming in FastAPI becomes useful.
FastAPI is built on top of asynchronous Python frameworks like Starlette and runs on ASGI servers such as Uvicorn. Because of this, it supports Python’s async and await syntax out of the box.
But what does that actually mean? And when should you use it?
Let’s walk through it in simple terms.
What Is Asynchronous Programming in FastAPI?
In regular (synchronous) Python code, tasks run one after another.
Example flow:
-
Request arrives
-
Server processes it
-
Response is returned
-
Then the next request starts
This works fine for simple cases. But if the server is waiting for something — like:
-
a database query
-
a file read
-
an external API call
…it blocks the server from handling other requests.
Asynchronous programming solves this problem.
Instead of waiting idly, the server can pause a task and switch to another request. Once the waiting operation finishes, the server resumes the task.
That’s what async and await allow in Python.
Understanding async and await
Two keywords power asynchronous programming in Python:
async
Used to declare an asynchronous function.
return {"message": "Hello"}
await
Used to pause execution until a task completes.
In FastAPI, route handlers can be written using async def.
Basic FastAPI Async Example
Here’s a minimal example showing how an async FastAPI endpoint works.
from fastapi import FastAPI
import asyncio
app = FastAPI()
# Simulate a slow operation (like a database call)
async def fake_database_call():
await asyncio.sleep(2) # non-blocking delay
return {"data": "Result from database"}
@app.get("/items")
async def read_items():
# Await the async function
result = await fake_database_call()
return result
How this works
-
A request hits
/items -
fake_database_call()starts -
The server does not block while waiting
-
Other requests can be processed
-
After 2 seconds, the response returns
This is the core idea behind high-performance FastAPI APIs.
When to Use Async in FastAPI
Using async makes sense when your endpoint performs I/O-bound operations.
Examples:
-
database queries
-
external API calls
-
file system reads
-
network requests
For example:
-
async database drivers
-
async HTTP clients
-
async file operations
However, if your code performs heavy CPU work (like image processing or machine learning training), async won’t help much.
Simple Python Example Using a Built-In Dataset
Let’s look at a small example using a dataset from scikit-learn.
We’ll create a FastAPI endpoint that loads a dataset and returns basic information.
from fastapi import FastAPI
from sklearn.datasets import load_iris
import asyncio
app = FastAPI()
# Async function that simulates data processing
async def load_dataset():
await asyncio.sleep(1) # simulate delay
iris = load_iris()
return {
"samples": len(iris.data),
"features": iris.feature_names,
"target_names": iris.target_names.tolist()
}
@app.get("/dataset-info")
async def dataset_info():
# Await the async dataset loader
data = await load_dataset()
return data
What this example shows
-
load_iris()loads a built-in dataset -
asyncio.sleep()simulates a slow task -
FastAPI handles requests without blocking other users
Example response:
"samples": 150,
"features": ["sepal length", "sepal width", "petal length", "petal width"],
"target_names": ["setosa", "versicolor", "virginica"]
}
This type of pattern is common when building data APIs with FastAPI.
Sync vs Async in FastAPI
FastAPI actually supports both styles.
Synchronous route
def sync_route():
return {"message": "Regular function"}
Asynchronous route
async def async_route():
return {"message": "Async function"}
FastAPI automatically handles both.
But if your endpoint calls async libraries, then you should also use async def.