DEV Community

Cover image for Building Production-Ready APIs with FastAPI: The Modern Python Framework You Should Be Using
MEROLINE LIZLENT
MEROLINE LIZLENT

Posted on

Building Production-Ready APIs with FastAPI: The Modern Python Framework You Should Be Using

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]
Enter fullscreen mode Exit fullscreen mode

Your simplest possible app:

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"message": "Hello, FastAPI!"}
Enter fullscreen mode Exit fullscreen mode

Run it:

uvicorn main:app --reload
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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"}
Enter fullscreen mode Exit fullscreen mode
# 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)
Enter fullscreen mode Exit fullscreen mode

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"
Enter fullscreen mode Exit fullscreen mode

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)