This is a cheatsheet — copy-paste-ready snippets for the routing layer. For the long-form treatment, see Chapter 2 of the textbook .

Path operation decorators

from fastapi import FastAPI

app = FastAPI()

@app.get("/items/{id}")
async def read(id: int): ...

@app.post("/items")
async def create(): ...

@app.put("/items/{id}")
async def replace(id: int): ...

@app.patch("/items/{id}")
async def update(id: int): ...

@app.delete("/items/{id}")
async def remove(id: int): ...

@app.head("/items")
async def head(): ...

@app.options("/items")
async def options(): ...

@app.trace("/items")
async def trace(): ...

@app.api_route("/multi", methods=["GET", "POST"])
async def multi(): ...

Decorators return the original function. Test handlers as plain coroutines without the framework — handy for unit testing.

Path parameters

@app.get("/items/{id}")
async def get_item(id: int): ...

@app.get("/users/{user_id}/posts/{post_id}")
async def get_post(user_id: int, post_id: int): ...

# Path-converters
@app.get("/files/{p:path}")
async def file(p: str): ...   # matches slashes too

# Enum constraint
from enum import Enum
class Color(str, Enum):
    R = "red"; G = "green"; B = "blue"

@app.get("/c/{color}")
async def c(color: Color): ...   # only red/green/blue

# Path() metadata
from fastapi import Path
@app.get("/items/{id}")
async def get(id: int = Path(..., ge=1, le=10**9)): ...

Query parameters

@app.get("/search")
async def s(q: str, limit: int = 20, offset: int = 0):
    ...

# Optional
async def s(q: str | None = None): ...

# With Query() metadata
from fastapi import Query
async def s(
    q: str | None = Query(None, min_length=2, max_length=100, pattern=r"^[a-z ]+$"),
    tags: list[str] = Query(default_factory=list, description="filter tags"),
    page: int = Query(1, ge=1, le=10000),
): ...

# alias / deprecated
async def s(q: str | None = Query(None, alias="search-query", deprecated=True)): ...

Lists in query string: ?tags=a&tags=b&tags=ctags=["a", "b", "c"].

Body

from pydantic import BaseModel

class UserIn(BaseModel):
    email: str
    name: str

@app.post("/users")
async def create(user: UserIn): ...

# Multiple bodies → wrapped: {"user": {...}, "importance": 5}
from fastapi import Body
@app.put("/users/{id}")
async def update(id: int, user: UserIn, importance: int = Body(...)): ...

# Embed single body to wrap it
async def update(user: UserIn = Body(..., embed=True)): ...

# Raw body
from fastapi import Request
@app.post("/raw")
async def raw(req: Request):
    body = await req.body()

Headers, cookies

from fastapi import Header, Cookie

@app.get("/h")
async def h(user_agent: str | None = Header(None)): ...
# convert_underscores by default: user_agent -> User-Agent

async def h(x_token: str = Header(...)): ...

@app.get("/c")
async def c(session: str | None = Cookie(None)): ...

# Setting cookies
from fastapi import Response
@app.post("/login")
async def login(response: Response):
    response.set_cookie("session", "abc", httponly=True, secure=True, samesite="lax")
    return {"ok": True}

Forms and files

from fastapi import Form, File, UploadFile

@app.post("/login")
async def login(username: str = Form(...), password: str = Form(...)): ...

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

@app.post("/multi")
async def multi(files: list[UploadFile]): ...

UploadFile streams; prefer over bytes. Requires python-multipart.

Routers

# api/users.py
from fastapi import APIRouter
router = APIRouter(prefix="/users", tags=["users"])

@router.get("/")
async def list_(): ...

@router.get("/{id}")
async def get(id: int): ...

# main.py
from fastapi import FastAPI
from .api import users
app = FastAPI()
app.include_router(users.router)

include_router options

app.include_router(
    users.router,
    prefix="/api/v1",
    tags=["v1", "users"],
    dependencies=[Depends(verify_api_key)],
    responses={404: {"description": "Not found"}},
    deprecated=False,
    include_in_schema=True,
)

Nested routers

api = APIRouter(prefix="/api/v1")
api.include_router(users.router)
api.include_router(posts.router)
app.include_router(api)

Mounts

from fastapi.staticfiles import StaticFiles

app.mount("/static", StaticFiles(directory="static"), name="static")
app.mount("/admin", admin_app)         # any ASGI app

Path operation kwargs

@app.post(
    "/users",
    response_model=UserOut,
    status_code=201,
    tags=["users"],
    summary="Create a user",
    description="Long description...",
    response_description="Created user",
    responses={400: {"description": "Bad input"}, 409: {"description": "Conflict"}},
    deprecated=False,
    operation_id="create_user",
    include_in_schema=True,
)
async def create(...): ...

URL generation

url = app.url_path_for("create_user", )      # by function name (or operation_id)
url = router.url_path_for("get", id=42)

Custom APIRoute (per-route hook)

from fastapi.routing import APIRoute

class TimedRoute(APIRoute):
    def get_route_handler(self):
        original = super().get_route_handler()
        async def custom(request):
            t = time.perf_counter()
            try:
                return await original(request)
            finally:
                log.info("dur_ms", v=(time.perf_counter() - t) * 1000)
        return custom

router = APIRouter(route_class=TimedRoute)

Powerful escape hatch for cross-cutting concerns.

Middleware

@app.middleware("http")
async def rid(req, call_next):
    rid = req.headers.get("x-request-id") or uuid.uuid4().hex
    resp = await call_next(req)
    resp.headers["x-request-id"] = rid
    return resp

Exceptions

from fastapi import HTTPException

@app.get("/x/{id}")
async def x(id: int):
    if id < 0:
        raise HTTPException(404, "not found")

# Custom exception handler
class AppError(Exception): ...
@app.exception_handler(AppError)
async def handle(req, exc):
    return JSONResponse({"err": "bad"}, status_code=400)

Read this next

If you want my FastAPI starter (this layout, fully wired), it’s at rajpoot.dev .


Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .