Capstone: A Complete, Working Food Tracker
Food Tracker (FastAPI)
Chapter 12 · Capstone: A Complete, Working Food Tracker
Priya keeps her own household pantry tracked with the app this course just spent eleven chapters building — one FastAPI process, one SQLite file, one deliberately plain frontend, all deployed and running for real. What follows is one ordinary session with it.
Before touching the frontend at all, Priya's own developer opens /docs to confirm the deployment is healthy — Chapter 1's own praised automatic API explorer, still available in this deployment since it was left enabled deliberately, per Chapter 11's own decision.
Priya scans the barcode. Chapter 4's plain-function startScanner decodes it client-side, calling Chapter 3's genuinely async lookup route — httpx.AsyncClient checking BarcodeCache first, falling back to a live Open Food Facts call, with the event loop free to serve other requests the whole time it waits.
The add-item view pre-fills from that lookup. Priya sets the expiry date and submits; Chapter 5's ItemCreate schema validates the request before create_item's own body ever runs — no hand-written checks anywhere in that route, because there's nothing left to check by the time it executes.
Chapter 6's alerts route surfaces the eggs once they're within three days of expiring — a real typed Date column comparison, correct regardless of formatting, and a plain def since a local SQLite query never needed async in the first place.
Priya searches "egg." Chapter 7's ilike() finds it instantly — no shadow field, the same real advantage Django and Express both claim in their own courses.
The eggs get used up. Chapter 8's PATCH /{item_id}/use clears expiry_date and guards against a duplicate click, then Chapter 10's BackgroundTasks schedules a UsageLog entry — written after the response is already back in Priya's browser, using its own dedicated session since the request's own is already closed by then.
The same markUsed function calls loadAlerts() and loadRecipeSuggestions() directly, by name — Chapter 8's own honest point still holds: this app never needed anything like the Express sibling's own Context, because there's no component tree creating that coordination problem in the first place.
With chicken and spinach both nearing expiry, Chapter 9's asyncio.gather() fans out to TheMealDB concurrently — each lookup_ingredient call opening its own dedicated session, genuinely safe under concurrency, unlike the bug that pattern was specifically designed to avoid — ranked by how many expiring ingredients each recipe actually uses.
Priya's whole session runs against a real deployment: gunicorn managing several Uvicorn workers, no CORS configuration anywhere since the frontend has shared FastAPI's own origin since Chapter 1, worker count kept modest specifically to avoid the SQLite lock contention Chapter 11 named honestly, and nginx terminating HTTPS in front of it all.
Chapter Attribution
| Step | Chapter(s) applied |
|---|---|
| 1 — /docs | Chapter 1 (automatic docs), Chapter 11 (deliberately kept enabled) |
| 2 — Scanning | Chapter 4 (camera scanning), Chapter 3 (async lookup + BarcodeCache) |
| 3 — Adding the item | Chapter 5 (ItemCreate validation), Chapter 2 (schema, INSERT) |
| 4 — Expiry alert | Chapter 6 (typed Date comparison, plain def) |
| 5 — Search | Chapter 7 (ilike(), native case-insensitive substring match) |
| 6 — Marking used + logging | Chapter 8 (PATCH, active-only guard), Chapter 10 (BackgroundTasks, dedicated session) |
| 7 — Direct updates | Chapter 8 (no Context needed) |
| 8 — Recipe suggestion | Chapter 9 (asyncio.gather(), per-task session, relevance sort) |
| 9 — Real deployment | Chapter 11 (gunicorn, no CORS, worker count, TLS) |
/docs, praised in Chapter 1, got an honest second look in Chapter 11 before Step 1 could responsibly happen at all. This course's own throughline was never "FastAPI does everything automatically" — it was "here's exactly where FastAPI's own design choices pay off, and here's exactly where they don't," the same calibrated honesty every course in this quartet has tried to hold itself to.
BackgroundTasks is not a durable job queue was never resolved with a real job queue; and no automated test suite or CI pipeline was covered anywhere in this course.
Hands-On Exercises
Trace Step 6 in detail: explain what happens to the response Priya's browser receives versus what happens afterward in the background, and why the background portion needs its own database session.
📄 View solutionPick any two steps from Priya's session and explain how each one depends on at least two earlier chapters working together, not just one chapter in isolation.
📄 View solutionExplain the finding-box's own claim that none of Chapter 1's three opening claims (async-native, Pydantic validation, automatic docs) were treated as unconditional wins across this course. Give one concrete example for each of the three claims.
📄 View solutionChapter 12 Quick Reference — Course Complete
- 9 steps, 11 prior chapters — one continuous, realistic session with the finished, deployed app
- This course's own throughline, closed out: exactly where FastAPI's own design pays off, and exactly where it doesn't — never an unconditional win
- Honest scope note: no meal planner, no offline/PWA, no multi-user ownership model (same gap as Django and Express), TheMealDB matching remains best-effort, BackgroundTasks isn't durable, no automated tests/CI
- Food Tracker (FastAPI) is now complete — 12/12 chapters
- The entire Food Tracker Quartet is now complete — 4 courses, 50 chapters, one app, four architectures