mrkeyoor.com_
Wed 05 Aug 05:01 UTC
PyPIWeb Backendupdated 05 Aug 2026

fastapi

The dominant modern Python web framework for building APIs. You declare endpoint parameters and request bodies with standard Python type hints, and FastAPI handles validation, serialization, editor autocomplete, and an auto-generated OpenAPI spec with interactive Swagger and ReDoc pages for free. Under the hood it is a thin layer over Starlette (ASGI web parts) and Pydantic (data validation), which is where its speed comes from. It runs sync or async endpoints side by side and has become the default choice for Python API services, especially around ML model serving.

Verdict

The right default for Python JSON APIs in 2026: the type-hint-driven developer experience is real and the docs walk you through everything. Just respect the async footguns, expect to assemble your own stack around it, and do not pick it for content-heavy sites that Django solves in an afternoon.

API stability3/5Core decorator API has barely changed in years, but the project is still 0.x with breaking changes allowed in minors, and the Pydantic v2 transition showed migrations can be substantial
Docs5/5fastapi.tiangolo.com is a full tutorial-style course covering every feature with runnable examples in multiple languages; among the best docs of any web framework
Maintenance4/5Pushed the day of this review with frequent releases (0.141.1 current) and only 76 open issues and PRs on a 101k-star repo; offset by governance still concentrated around the creator and his new company
Ecosystem4/5Around 146M weekly downloads and huge mindshare, with SQLModel, Starlette, and Pydantic ecosystems behind it; still no Django-scale battery ecosystem, you assemble auth/ORM/jobs yourself

Use it if

  • You are building a JSON API in Python and want request validation and OpenAPI docs generated from type hints instead of maintained by hand
  • You need async I/O: it is ASGI-native, so websockets, streaming, and high-concurrency I/O-bound endpoints work without bolt-ons
  • You serve ML models: type-hinted request schemas plus async workers is why teams at Uber and Netflix cited it, per the README
  • You already use Pydantic models elsewhere and want the same models to define your API contract
Skip it if

Setup reality

pip install "fastapi[standard]" gets you the framework plus uvicorn and the fastapi dev CLI; plain pip install fastapi leaves you to install a server yourself, which trips up beginners. Development is genuinely pleasant (fastapi dev auto-reloads, /docs is instant gratification). Production is more assembly: uvicorn workers behind a process manager or gunicorn, plus your own choices for ORM, migrations, auth, and background jobs, none of which are included. The big conceptual tax is def vs async def: sync endpoints run in a threadpool, async ones on the event loop, and mixing blocking libraries into async def is an easy, quiet way to wreck throughput.

Patterns

Minimal app with typed paramshello-api

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{item_id}")
def read_item(item_id: int, q: str | None = None):
    return {"item_id": item_id, "q": q}

Run with: fastapi dev main.py (from fastapi[standard]); path params come from the URL, other args become query params.

Validate a JSON body with Pydanticrequest-body

from pydantic import BaseModel

class Item(BaseModel):
    name: str
    price: float
    is_offer: bool | None = None

@app.post("/items/")
def create_item(item: Item):
    return {"name": item.name, "price": item.price}

Invalid payloads get an automatic 422 with field-level errors; you write no validation code.

Filter output with a response modelresponse-model

class UserOut(BaseModel):
    id: int
    email: str

@app.get("/users/{user_id}", response_model=UserOut)
def get_user(user_id: int):
    user = fetch_user(user_id)  # may contain hashed_password etc.
    return user

response_model strips fields not in the schema, which is the supported way to keep secrets out of responses.

Choose def vs async def correctlysync-vs-async

import httpx

@app.get("/sync")
def sync_endpoint():
    return requests.get(url).json()  # ok: runs in threadpool

@app.get("/async")
async def async_endpoint():
    async with httpx.AsyncClient() as client:
        r = await client.get(url)  # ok: truly async
    return r.json()

Never call blocking libraries inside async def; it blocks the event loop for every request. Plain def is the safe default.

Share logic with Dependsdependency-injection

from fastapi import Depends
from typing import Annotated

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@app.get("/orders")
def list_orders(db: Annotated[Session, Depends(get_db)]):
    return db.query(Order).all()

yield dependencies run cleanup after the response; the Annotated form is the current documented style.

Protect endpoints with an OAuth2 bearer tokenauth-bearer

from fastapi import Depends, HTTPException
from fastapi.security import OAuth2PasswordBearer

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")

async def current_user(token: Annotated[str, Depends(oauth2_scheme)]):
    user = decode_token(token)
    if not user:
        raise HTTPException(status_code=401)
    return user

@app.get("/me")
async def me(user: Annotated[User, Depends(current_user)]):
    return user

This only extracts and gates on the token; issuing JWTs, hashing passwords, and refresh flows are your code (the docs have a full recipe).

Return proper HTTP errorserror-handling

from fastapi import HTTPException

@app.get("/items/{item_id}")
def read_item(item_id: int):
    item = store.get(item_id)
    if item is None:
        raise HTTPException(status_code=404, detail="Item not found")
    return item

Raise HTTPException, do not return it; returning one serializes the exception object as a 200 body.

Accept a file uploadfile-upload

from fastapi import UploadFile

@app.post("/upload")
async def upload(file: UploadFile):
    contents = await file.read()
    return {"filename": file.filename, "size": len(contents)}

UploadFile requires python-multipart installed (included in fastapi[standard]); file.read() loads it all into memory, stream large files instead.

Run work after the responsebackground-tasks

from fastapi import BackgroundTasks

@app.post("/signup")
def signup(email: str, background_tasks: BackgroundTasks):
    background_tasks.add_task(send_welcome_email, email)
    return {"status": "ok"}

Tasks run in-process after the response and die with the worker; for retries or heavy jobs use Celery or another real queue.

Enable CORS for a frontendcors

from fastapi.middleware.cors import CORSMiddleware

app.add_middleware(
    CORSMiddleware,
    allow_origins=["https://app.example.com"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

allow_origins=["*"] cannot be combined with allow_credentials=True; browsers will reject the response.

Test with TestClienttest-endpoints

from fastapi.testclient import TestClient
from main import app

client = TestClient(app)

def test_read_item():
    r = client.get("/items/5?q=x")
    assert r.status_code == 200
    assert r.json() == {"item_id": 5, "q": "x"}

TestClient needs httpx installed; it calls the app in-process, no server required, and works for sync tests of async endpoints.

Run in productionrun-production

fastapi run main.py --workers 4 --port 8000
# or explicitly:
uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4

fastapi dev is development-only (auto-reload, localhost); put a reverse proxy in front and size workers to CPU cores.

Alternatives

PackageRegistryPick it when
djangoPyPIFull-stack apps that want ORM, admin, auth, and migrations included, with DRF or Django Ninja for the API layer
flaskPyPISmall sync services or teams that want a minimal, decades-stable WSGI framework and will add validation themselves
litestarPyPIA similar typed ASGI framework governed by a maintainer team instead of one lead, with built-in DTOs
starlettePyPIYou want the bare ASGI toolkit FastAPI is built on, without validation or OpenAPI machinery