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.

Step 1 — Checking /docs First

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.

Step 2 — Scanning a Carton of Eggs

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.

Step 3 — Adding It

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.

Step 4 — An Alert, a Few Days Later

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.

Step 5 — Searching for Something Bought Before

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.

Step 6 — Marking Them Used, and a Quiet Log Entry

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.

Step 7 — Everything Updates, Directly

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.

Step 8 — A Recipe Suggestion

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.

Step 9 — All of This, Actually Deployed

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

StepChapter(s) applied
1 — /docsChapter 1 (automatic docs), Chapter 11 (deliberately kept enabled)
2 — ScanningChapter 4 (camera scanning), Chapter 3 (async lookup + BarcodeCache)
3 — Adding the itemChapter 5 (ItemCreate validation), Chapter 2 (schema, INSERT)
4 — Expiry alertChapter 6 (typed Date comparison, plain def)
5 — SearchChapter 7 (ilike(), native case-insensitive substring match)
6 — Marking used + loggingChapter 8 (PATCH, active-only guard), Chapter 10 (BackgroundTasks, dedicated session)
7 — Direct updatesChapter 8 (no Context needed)
8 — Recipe suggestionChapter 9 (asyncio.gather(), per-task session, relevance sort)
9 — Real deploymentChapter 11 (gunicorn, no CORS, worker count, TLS)
What this whole course was really about
Chapter 1 opened with three concrete claims about FastAPI: async-native, Pydantic validation built in, automatic docs. Every step above tested one of those claims against a real scenario, and none of them were treated as unconditional wins. Async genuinely paid off in Steps 2 and 8 (a real network wait, a real concurrent fan-out) and honestly didn't in Step 4, exactly as Chapter 6 said it wouldn't. Pydantic validation eliminated an entire category of hand-written checks in Steps 3 and 6. And /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.
Honest scope note
This capstone deliberately stops short of several things: the weekly meal planner named as future work all the way back in Chapter 1 was never built; there's no offline/PWA support; no multi-user ownership model exists anywhere in this course — the same honest gap named in both the Django and Express siblings' own capstones, unlike the Firebase sibling's own real per-user authentication; Chapter 9's own limit on matching generic branded product names against TheMealDB's fixed vocabulary was never solved, only named; Chapter 10's own honest point that 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.
The Food Tracker Quartet is now complete
This closes out all four Food Tracker courses — the same app, deliberately rebuilt four times in four genuinely different architectures: Django's batteries-included MVT model, React + Express's full-JavaScript stack, React + Firebase's backend-as-a-service model, and this course's own async-native FastAPI backend with a deliberately plain vanilla-JS frontend. Fifty chapters in total, each course naming its own real advantages and honest limits rather than declaring any single architecture simply "the best" one.

Hands-On Exercises

Exercise 1

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 solution
Exercise 2

Pick 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 solution
Exercise 3

Explain 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 solution

Chapter 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