FastAPI has become the default choice for new Python REST APIs, largely because it gets three things right at once: it’s genuinely fast (built on Starlette and Pydantic), it generates interactive API documentation automatically, and it catches type errors before they become runtime bugs.
A Minimal API
pip install fastapi uvicorn
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
price: float
in_stock: bool = True
items: dict[int, Item] = {}
@app.get("/items/{item_id}")
def get_item(item_id: int):
if item_id not in items:
raise HTTPException(status_code=404, detail="Item not found")
return items[item_id]
@app.post("/items/{item_id}")
def create_item(item_id: int, item: Item):
items[item_id] = item
return {"id": item_id, "item": item}
Running It
uvicorn main:app --reload # Interactive docs are generated automatically at: # http://127.0.0.1:8000/docs
Why the Pydantic Models Matter
Declaring Item as a Pydantic model isn’t just documentation — FastAPI validates every incoming request against it automatically. Send a request missing the price field or with a string where a float is expected, and you get a clear 422 error with details, before your own code ever runs.
Adding Async for I/O-Bound Endpoints
import httpx
@app.get("/weather/{city}")
async def get_weather(city: str):
async with httpx.AsyncClient() as client:
resp = await client.get(f"https://api.example.com/weather/{city}")
return resp.json()
Making an endpoint async def lets FastAPI serve other requests while this one is waiting on a slow network call — genuinely useful once your API starts calling other services rather than just an in-memory dict.