If you are still using Flask or Django REST Framework to develop REST APIs but have yet to discover FastAPI, you are missing out on some real performance and developer experience. For all the right reasons, FastAPI is one of the most starred Python projects on GitHub—and for a reason, it's more than just that.
Let's get very deep into how it works and let's make something real on the way.
** What Is FastAPI?**
FastAPI is a new, high-performance Python-based web framework designed to create APIs. It is built on top of Starlette (for web parts) and Pydantic (for data validation) and natively supports Python's async/await syntax.
Key selling points:
Benchmarks: On par with NodeJS and Go
Swagger UI and ReDoc: Automatic docs out of the box.
Type safety: Type annotations for validation and editor assistance in Python:
Async-first: First-class support for async request handlers.
Getting Started
pip install fastapi uvicorn[standard]
Your simplest possible app:
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
def read_root():
return {"message": "Hello, FastAPI!"}
Run it:
uvicorn main:app --reload
Visit http://127.0.0.1:8000/docs — you already have interactive Swagger docs. No extra config needed.
The Power of Pydantic Models
That's where FastAPI can help. Your request/response schemas are defined in Pydantic models and FastAPI is responsible for validation, serialization, and documentation.
from fastapi import FastAPI
from pydantic import BaseModel, EmailStr
from typing import Optional
app = FastAPI()
class UserCreate(BaseModel):
name: str
email: EmailStr
age: Optional[int] = None
class UserResponse(BaseModel):
id: int
name: str
email: str
@app.post("/users", response_model=UserResponse, status_code=201)
def create_user(user: UserCreate):
# Simulate saving to DB
return UserResponse(id=1, name=user.name, email=user.email)
FastAPI returns detailed responses for a malformed email or missing name (422 – Unprocessable Entity) – without any extra code.
Path Parameters, Query Parameters, and Dependency Injection
from fastapi import FastAPI, Query, Depends, HTTPException
app = FastAPI()
# Path parameter
@app.get("/items/{item_id}")
def get_item(item_id: int):
if item_id > 100:
raise HTTPException(status_code=404, detail="Item not found")
return {"item_id": item_id}
# Query parameters with validation
@app.get("/search")
def search_items(
q: str = Query(..., min_length=3, max_length=50),
skip: int = Query(0, ge=0),
limit: int = Query(10, le=100),
):
return {"query": q, "skip": skip, "limit": limit}
Notice ge=0 (greater than or equal to) and le=100 (less than or equal to) — FastAPI validates these automatically.
Dependency Injection
FastAPI has a clean, built-in dependency injection system. It's great for things like database sessions, authentication, and shared logic.
from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session
from database import SessionLocal # your SQLAlchemy setup
app = FastAPI()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.get("/users/{user_id}")
def read_user(user_id: int, db: Session = Depends(get_db)):
user = db.query(User).filter(User.id == user_id).first()
if not user:
raise HTTPException(status_code=404, detail="User not found")
return user
The get_db function is a dependency, FastAPI calls it before your route handler and cleans up after.
Background Tasks
Do you need to send an email or log something after returning a response? FastAPI's BackgroundTasks is perfect:
from fastapi import FastAPI, BackgroundTasks
app = FastAPI()
def send_welcome_email(email: str):
# Your email logic here
print(f"Sending welcome email to {email}")
@app.post("/register")
def register(email: str, background_tasks: BackgroundTasks):
background_tasks.add_task(send_welcome_email, email)
return {"message": "Registered successfully"}
The response is returned immediately the email is sent in the background.
Middleware
Add custom middleware for things like logging, CORS, or timing:
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
import time
app = FastAPI()
# CORS
app.add_middleware(
CORSMiddleware,
allow_origins=["https://yourfrontend.com"],
allow_methods=["*"],
allow_headers=["*"],
)
# Custom timing middleware
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
start = time.time()
response = await call_next(request)
duration = time.time() - start
response.headers["X-Process-Time"] = str(duration)
return response
Routers: Structuring Larger Applications
Don't put everything in main.py. Use APIRouter to split your app:
# routers/users.py
from fastapi import APIRouter
router = APIRouter(prefix="/users", tags=["users"])
@router.get("/")
def list_users():
return [{"id": 1, "name": "Alice"}]
@router.get("/{user_id}")
def get_user(user_id: int):
return {"id": user_id, "name": "Alice"}
# main.py
from fastapi import FastAPI
from routers import users, products, auth
app = FastAPI()
app.include_router(users.router)
app.include_router(products.router)
app.include_router(auth.router)
Testing FastAPI Apps
FastAPI integrates nicely with pytest via TestClient:
from fastapi.testclient import TestClient
from main import app
client = TestClient(app)
def test_create_user():
response = client.post("/users", json={"name": "Bob", "email": "bob@example.com"})
assert response.status_code == 201
assert response.json()["name"] == "Bob"
Summary
There is a sweet spot to FastAPI: it's fast enough for production, ergonomic enough to get a prototype done fast, and typed enough to keep large bases maintainable. Saving hours of documentation work per project alone with the auto-generated docs.
As of today, unless you have a strong reason to do otherwise, FastAPI should be your top choice if you are building a new Python API project.
Top comments (0)