Skip to course content
Free FastAPI backend course

FastAPI for AI Backend Development

Unit 04.03: Combining all three in one endpoint

One handler signature can draw from four different parts of the request.

Path, query, body and header together

An endpoint taking two path segments, a query flag, a body and a header.

The code calls it with all four.

from fastapi import FastAPI, Header, Query
from fastapi.testclient import TestClient
from pydantic import BaseModel

app = FastAPI()


class Update(BaseModel):
    status: str


@app.patch("/accounts/{account_id}/invoices/{invoice_id}")
def update(account_id: str, invoice_id: str, body: Update,
           notify: bool = Query(default=False),
           x_request_id: str | None = Header(default=None)) -> dict:
    return {"account": account_id, "invoice": invoice_id,
            "status": body.status, "notify": notify, "request_id": x_request_id}


client = TestClient(app)
r = client.patch("/accounts/ACC-1/invoices/INV-9?notify=true",
                 json={"status": "paid"}, headers={"X-Request-ID": "r-8841"})
print(r.json())

print("\nFour sources, one signature: two path parts, a query flag, a body,")
print("and a header. FastAPI decides which is which from the declaration.")

FastAPI decides which is which from the declaration: names matching the path template are path parameters, Pydantic models are the body, and Header marks a header. Nothing is positional or guessed.

The header name conversion is worth knowing - x_request_id in Python corresponds to X-Request-ID on the wire, because hyphens are not valid in an identifier.

The mistake this prevents

The mistake is reading the raw request object to get at a header or a query value. It works and it removes the value from the schema, from the validation and from the documentation all at once.

Takeaway

Declare every input in the signature. FastAPI resolves the source from the declaration, and anything read from the raw request is invisible to the contract.