An API with validation, using FastAPI
🎯You'll be able to
Build a prediction endpoint with FastAPI and pydantic that rejects missing, unknown and impossible inputs before the model sees them.
🏠The everyday picture
A bank counter checks the form before processing it: is every field filled, is the date real, is the amount positive? It sends back a form with a clear note rather than processing nonsense.
📘What it really is
-
FastAPI is a Python web framework. You describe a request body as a pydantic model: field names, types and limits. FastAPI checks each request against it and returns
422 Unprocessable Entitywith the reasons if it doesn't fit. -
What to check:
- every required field is there;
- categories are from the allowed list (
Literal['JAI', 'JOD', 'UDR']); - numbers are in a sensible range (
Field(gt=0, le=30)for weight); -
no extra fields (
extra='forbid'), which also keeps out personal data the model doesn't need (ML7 Lesson 3). -
Why it matters for models: a model will happily score nonsense. A negative weight or a zone of "moon" gets a probability, which looks like an answer. Validation makes bad inputs fail loudly.
-
The response: give the probability, the decision at the threshold, the threshold itself, the model version and a request id, so a caller can log and trace it.
-
Docs for free: FastAPI serves interactive documentation at
/docs, generated from the same schema.
📝A worked example
Input: the reference solution's Shipment model in dakdost/serve.py, and two requests.
uvicorn dakdost.serve:app --port 8000
curl -s localhost:8000/predict -H 'content-type: application/json' -d '{"booked_at": "2026-06-12T18:40+05:30", "origin_hub": "JAI",
"dest_zone": "remote", "distance_km": 640, "weight_kg": 2.5, "service": "standard", "pickup_slot": "evening", "address_quality": 0.31,
"hub_load": 52, "payment": "cod", "promised_days": 6}'
Expected output (2 November 2026): {"probability_late": 0.8533, "likely_late": true, "threshold": 0.3, "model_version": "2026.07.1",
"request_id": "…"}. The same request without address_quality returns 422 with "type": "missing" and "loc": ["body",
"address_quality"]. A weight_kg of −1 returns 422 with "Input should be greater than 0".
🌍Where it's used
- Any model API: schemas are the first line of defence.
- Shared contracts: the same schema documents the API for the app team.
- Security: rejecting unexpected fields stops some attacks and accidental data leaks.
📝Case scenario
A pricing model accepted a distance field in metres from one app and kilometres from another. Neither failed; prices were wildly wrong
for one app's users for a week. Adding a schema with a named unit (distance_km) and a range check, plus a test with each app's real
request, ended it.
🖱️Try it
Start the solution API (once Project 2 works) and send: a request with "dest_zone": "moon", one with "customer_phone" added, and one with
"address_quality": 1.5. Success: all three get 422 with a reason you could show to the app developer.
⚠️Common mistakes
- Accepting any JSON. Sign:
predict(shipment: dict). Fix: a pydantic model with types and limits. - Only types, no ranges. Sign: a 900 kg parcel gets a probability. Fix:
Fieldlimits from the real rules. - Hiding the version. Sign: nobody knows which model answered. Fix: return and log the model version.
🛠️For your project
Project 2's tests 2, 3 and 4 check the response fields and that missing, unknown, impossible and personal inputs are rejected.
✅Quick check
What does FastAPI return when a request doesn't match the schema?
Show the answer
422, with the reasons for each field.
Why forbid extra fields?
Show the answer
So inputs the model doesn't expect, including personal data, are rejected instead of being passed on or logged.
