Building APIs with FastAPI

FastAPI is a modern Python framework for building APIs. It uses type hints to validate requests, serialise responses and generate interactive OpenAPI documentation automatically. It suits microservices, ML model serving and any API-first project that doesn't need Django's full stack.

Setup

python -m venv .venv && source .venv/bin/activate
pip install "fastapi[standard]"
fastapi dev main.py        # auto-reloading dev server on http://127.0.0.1:8000

A first API with Pydantic models

# main.py
from decimal import Decimal
from fastapi import FastAPI, HTTPException, status
from pydantic import BaseModel, Field

app = FastAPI(title="Mithai Shop API", version="1.0.0")

class SweetIn(BaseModel):
    name: str = Field(min_length=2, max_length=60)
    price_per_kg: Decimal = Field(gt=0)
    veg: bool = True

class SweetOut(SweetIn):
    id: int

DB: dict[int, SweetOut] = {}

@app.get("/sweets", response_model=list[SweetOut])
def list_sweets(max_price: Decimal | None = None, limit: int = 20):
    items = list(DB.values())
    if max_price is not None:
        items = [s for s in items if s.price_per_kg <= max_price]
    return items[:limit]

@app.get("/sweets/{sweet_id}", response_model=SweetOut)
def get_sweet(sweet_id: int):
    if sweet_id not in DB:
        raise HTTPException(status_code=404, detail="Sweet not found")
    return DB[sweet_id]

@app.post("/sweets", response_model=SweetOut, status_code=status.HTTP_201_CREATED)
def create_sweet(payload: SweetIn):
    new_id = max(DB, default=0) + 1
    DB[new_id] = SweetOut(id=new_id, **payload.model_dump())
    return DB[new_id]

Open http://127.0.0.1:8000/docs and you get Swagger UI where you can try every endpoint.

Where parameters come from

Declared as FastAPI reads it from
Name in the path {sweet_id} Path parameter
Simple type not in path (limit: int = 20) Query string
Pydantic model (payload: SweetIn) JSON body
Header(), Cookie() Headers and cookies

If a client sends {"name": "K", "price_per_kg": -5}, FastAPI returns 422 with a list of exactly which fields failed and why — no validation code written by you.

Dependencies

Depends injects shared logic such as database sessions or authentication.

from fastapi import Depends, Header

def require_api_key(x_api_key: str = Header()):
    if x_api_key != "load-from-env":
        raise HTTPException(status_code=401, detail="Invalid API key")

@app.delete("/sweets/{sweet_id}", status_code=204, dependencies=[Depends(require_api_key)])
def delete_sweet(sweet_id: int):
    DB.pop(sweet_id, None)

The header parameter x_api_key maps to the X-API-Key header automatically (underscores become hyphens).

FastAPI or Django REST Framework?

FastAPI DRF
Strength Speed, async, type-driven validation Django ORM, admin, auth, mature ecosystem
Docs Built-in OpenAPI Via drf-spectacular
Best for Microservices, ML inference Full apps already on Django

Key takeaways

  • Type hints plus Pydantic models give you validation and docs for free.
  • Use separate input and output models so internal fields never leak.
  • Raise HTTPException with the right status code; use Depends for cross-cutting concerns.
  • Run with fastapi run (Uvicorn) in production behind a reverse proxy.

Practice

Extend the sweets API with PATCH /sweets/{id} using a model whose fields are all optional, a category query filter, and an API-key dependency on every write endpoint. Test each endpoint from the /docs page.

B

Written by Bablu Kumar Jha

Founder, BeeProton AI · Mumbai, Maharashtra, India