Skip to course content
Free generative AI app course

Generative AI Application Development with Python

Unit 10.01: Validating the request before the model sees it

Validation belongs on the request model, where it runs before any of your code.

Length bounds and a content rule

A field validator that rejects card numbers alongside length bounds.

The code posts three payloads.

from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field, field_validator

app = FastAPI()


class Ask(BaseModel):
    question: str = Field(min_length=1, max_length=4000)

    @field_validator("question")
    @classmethod
    def no_card_numbers(cls, v):
        digits = "".join(ch for ch in v if ch.isdigit())
        if len(digits) >= 16:
            raise ValueError("looks like it contains a card number")
        return v


@app.post("/ask")
def ask(body: Ask):
    return {"answer": "ok"}


client = TestClient(app)
for payload in [{"question": "How long?"}, {"question": ""},
                {"question": "my card is 4111 1111 1111 1111"}]:
    r = client.post("/ask", json=payload)
    print(f"{str(payload)[:44]:46} -> {r.status_code}")

# All three are rejected or accepted before the handler runs, so nothing
# invalid reaches the prompt, the provider, or your logs.

All three decisions happen before the handler body executes. The card number never reaches the prompt, the provider, or your logs - which is the same argument as Module 4, now enforced at the edge of the system.

The empty and oversized cases are rejected by declared bounds rather than by hand-written checks, so they cannot be forgotten in a new endpoint.

The mistake this prevents

The mistake is validating in the handler. It works for one endpoint, and the second endpoint gets a slightly different version of the same checks - which is how one path ends up without the card-number rule.

Takeaway

Put validation on the request model so it runs before your code and cannot be forgotten by a new endpoint.