Complete worked example
python
from typing import Annotated, Literal
from fastapi import Depends, FastAPI, Header, HTTPException, Query
from fastapi.testclient import TestClient
from pydantic import BaseModel
class PublicRole(BaseModel):
id: int
title: str
country: str
class Page(BaseModel):
observed_total: int
items: list[PublicRole]
def inspect_example(request):
records = [{"id": 1, "title": "QA", "country": "UA", "owner": "alice", "private_note": "a"},
{"id": 2, "title": "API", "country": "UA", "owner": "alice", "private_note": "b"},
{"id": 3, "title": "Data", "country": "UA", "owner": "bob", "private_note": "c"},
{"id": 4, "title": "Web", "country": "IL", "owner": "alice", "private_note": "d"}]
app = FastAPI()
def principal(x_demo_user: Annotated[str | None, Header()] = None):
if x_demo_user not in ("alice", "bob"):
raise HTTPException(401, "Sign in")
return x_demo_user
@app.get("/roles", response_model=Page)
def read(user: Annotated[str, Depends(principal)], country: Literal["UA", "IL"],
limit: Annotated[int, Query(ge=1, le=2)] = 1):
selected = sorted([r for r in records if r["owner"] == user and r["country"] == country],
key=lambda r: r["id"])
return {"observed_total": len(selected), "items": selected[:limit]}
headers = {} if "user" not in request else {"X-Demo-User": request["user"]}
with TestClient(app) as client:
response = client.get("/roles", params=request.get("query", {"country": "UA"}), headers=headers)
body = response.json()
if response.status_code == 422:
body = sorted({e["type"] for e in body["detail"]})
return {"status": response.status_code, "body": body}- Reasoning
- A demo X-Demo-User header maps to one of two explicitly allowed fixture identities. This is a local teaching boundary, not deployable authentication. Invalid or missing identities receive 401. The country vocabulary and positive bounded limit are validated independently. Selection first keeps records owned by the resolved user in the requested country, then sorts by ID and applies the limit. The response models exclude owner and private_note. observed_total counts that filtered owned collection before the page is taken, so it can exceed the number of returned items.