Unit 05.01: Validation that rejects before your code runs
Validation runs before your handler, so invalid input never reaches your code.
Three of four requests rejected, handler untouched
An endpoint that records every call it receives, with a constrained body.
The code posts four payloads and counts the handler runs.
from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
app = FastAPI()
CALLS = []
class Request(BaseModel):
text: str = Field(min_length=1, max_length=40)
@app.post("/classify")
def classify(body: Request) -> dict:
CALLS.append(body.text)
return {"category": "billing"}
client = TestClient(app)
for payload in [{"text": "charged twice"}, {"text": ""},
{"text": "x" * 100}, {}]:
r = client.post("/classify", json=payload)
print(f"{str(payload)[:24]:26} -> {r.status_code}")
print(f"\nhandler ran {len(CALLS)} time(s) out of 4 requests")
print("the three invalid payloads never reached your code")
The handler ran once out of four requests. The empty string, the over-length text and the missing field were each rejected with a 422 naming the offending field.
That call count is the useful demonstration. Your code never had to handle any of those cases, so there is no branch to write and none to forget.
The mistake this prevents
The mistake is defensive checks inside the handler duplicating what the model already guarantees. They are dead code that suggests the model cannot be trusted, and they drift out of step with it.
Takeaway
Constraints on the model mean invalid requests never reach your handler. Do not re-check inside what the model already guarantees.
