Python•30 min read•Advanced
Build a Backend with FastAPI
Build a typed, async, OpenAPI-documented Python REST API in minutes.
Why FastAPI?
FastAPI is a modern Python web framework that has, in just a few years, become the default choice for building APIs in Python. It is built on top of Starlette (an async web toolkit) and Pydantic (a runtime data validation library). Three things set it apart:
- Type-driven — your function signatures define your API. FastAPI uses your type hints to validate requests, serialize responses, and generate documentation.
- Async-first — fully native async/await support, so it can handle thousands of concurrent connections on one process.
- Auto-documented — interactive Swagger and ReDoc docs are generated for you, for free.
Installation
bash
python -m venv .venv
source .venv/bin/activate # or .venv\Scripts\activate on Windows
pip install fastapi uvicorn[standard]`fastapi` is the framework itself. `uvicorn` is the ASGI server that actually runs your app.
A complete tiny API
python
# main.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from uuid import uuid4
app = FastAPI(title="Todos API")
class TodoIn(BaseModel):
title: str
done: bool = False
class Todo(TodoIn):
id: str
DB: dict[str, Todo] = {}
@app.get("/todos", response_model=list[Todo])
def list_todos():
return list(DB.values())
@app.post("/todos", response_model=Todo, status_code=201)
def add_todo(payload: TodoIn):
todo = Todo(id=str(uuid4()), **payload.model_dump())
DB[todo.id] = todo
return todo
@app.get("/todos/{todo_id}", response_model=Todo)
def get_todo(todo_id: str):
if todo_id not in DB:
raise HTTPException(status_code=404, detail="Not found")
return DB[todo_id]
@app.delete("/todos/{todo_id}", status_code=204)
def delete_todo(todo_id: str):
DB.pop(todo_id, None)bash
uvicorn main:app --reloadThat's it — a fully typed, validating CRUD API. Open http://127.0.0.1:8000/docs and you'll find a Swagger UI with every endpoint, schema, and response code. You can fire requests right from the browser.
What's actually happening
- `@app.post("/todos")` — registers a handler for POST requests at /todos.
- `payload: TodoIn` — FastAPI sees the type hint, parses the JSON body, validates it against the Pydantic model, and gives you a typed `payload` object. If the body is malformed, the client gets a clean 422 error with details — you write zero validation code.
- `response_model=Todo` — the return value is converted to JSON matching that schema.
- `raise HTTPException(...)` — short-circuits the request with a proper HTTP error response.
Path and query parameters
python
from fastapi import Query
@app.get("/search")
def search(
q: str = Query(..., min_length=2, max_length=50),
limit: int = 10,
offset: int = 0,
):
return {"q": q, "limit": limit, "offset": offset}
# GET /search?q=ada&limit=5Async endpoints
Just declare the handler `async def` and use `await` inside. Best for endpoints that hit the database or call other services.
python
import httpx
@app.get("/github/{repo}")
async def github(repo: str):
async with httpx.AsyncClient() as client:
r = await client.get(f"https://api.github.com/repos/{repo}")
return r.json()Production checklist
- Move secrets and config to environment variables (use `pydantic-settings`).
- Add a real database — SQLAlchemy, SQLModel, or Tortoise ORM.
- Run behind Gunicorn + Uvicorn workers, or in a container.
- Add structured logging and request IDs.
- Add authentication (FastAPI has built-in OAuth2 / JWT helpers).
💡 Tip
FastAPI's docs at fastapi.tiangolo.com are some of the best in the industry. Read them sequentially — they'll teach you more than any tutorial.