FastAPI

Your first real API — types in, validation & docs for free · FastAPI 0.115+ · Pydantic v2

Cheat sheet · DocChat capstone · the framework reads your type hints to validate input and generate docs — so always type your params

Install & run

CommandWhat it does
pip install "fastapi[standard]"FastAPI + Uvicorn server + the fastapi CLI
fastapi dev main.pyDev server with auto-reload → http://127.0.0.1:8000
fastapi run main.pyProduction mode (no reload) — used in the Dockerfile
/docsAuto Swagger UI — try every endpoint in the browser
/redocAlternative auto docs (ReDoc)

Minimal app — main.py

from fastapi import FastAPI

app = FastAPI()

@app.get("/")
def read_root():
    return {"status": "ok"}      # dict -> JSON automatically

Path & query params

# Path param: declared in the URL, typed in the function
@app.get("/docs/{doc_id}")
def get_doc(doc_id: int):        # auto-cast + validated
    return {"doc_id": doc_id}

# Query params: any arg NOT in the path
# /search?q=cat&limit=10
@app.get("/search")
def search(q: str, limit: int = 10):
    return {"q": q, "limit": limit}
#   q       -> required (no default)
#   limit   -> optional, defaults to 10

Pydantic models (request body)

from pydantic import BaseModel, Field

class DocIn(BaseModel):              # request schema
    title: str
    body: str = Field(min_length=1)
    tags: list[str] = []

class DocOut(BaseModel):             # response schema
    id: int
    title: str

@app.post("/docs", response_model=DocOut)
def create_doc(doc: DocIn):
    # `doc` is parsed + validated from JSON body
    return {"id": 1, "title": doc.title}
#   response_model filters output to just id+title
This is the FastAPI superpower. Invalid JSON → automatic 422 with a clear error. No manual validation code.

Status codes & errors

from fastapi import FastAPI, HTTPException, status

@app.post("/docs", status_code=status.HTTP_201_CREATED)
def create_doc(doc: DocIn):
    return {"id": 1}

@app.get("/docs/{doc_id}")
def get_doc(doc_id: int):
    doc = db.get(doc_id)
    if doc is None:
        raise HTTPException(
            status_code=404,
            detail="Document not found",
        )
    return doc

Dependency injection — Depends

from fastapi import Depends

# A dependency is just a function FastAPI calls for you,
# injecting its return value into the endpoint.
def pagination(skip: int = 0, limit: int = 20):
    return {"skip": skip, "limit": limit}

@app.get("/docs")
def list_docs(page: dict = Depends(pagination)):
    return page

# Common use: auth — reject the request before the handler
def require_user(token: str | None = None):
    if token != "secret":
        raise HTTPException(401, "Not authenticated")
    return {"user": "sam"}

@app.get("/me")
def me(user: dict = Depends(require_user)):
    return user

SQLAlchemy session via Depends

The standard pattern: a generator dependency opens a session, yields it, and closes it after the response — even on error.

# db.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, Session

engine = create_engine("postgresql+psycopg://"
                       "user:pass@localhost/docchat")
SessionLocal = sessionmaker(bind=engine)

def get_db():
    db = SessionLocal()
    try:
        yield db          # hand session to the endpoint
    finally:
        db.close()        # always runs after response
# main.py
from fastapi import Depends
from sqlalchemy.orm import Session
from .db import get_db

@app.get("/docs/{doc_id}")
def get_doc(doc_id: int, db: Session = Depends(get_db)):
    doc = db.get(Document, doc_id)   # SQLAlchemy 2.0
    if doc is None:
        raise HTTPException(404, "Not found")
    return doc
One session per request. FastAPI runs get_db before the handler and the finally after — no leaked connections.

Quick reference

You wantHow
Read a route@app.get("/path") · also .post .put .patch .delete
Required query paramarg with no default: def f(q: str)
Optional query paramarg with default: def f(q: str = "") or q: str | None = None
JSON bodyarg typed as a BaseModel subclass
Shape the responseresponse_model=DocOut on the decorator
Set status codestatus_code=201 on the decorator
Return an errorraise HTTPException(404, "msg")
Share logic / auth / dbparam = Depends(func)
Group routes in filesAPIRouter()app.include_router(r)