Capstone: A Complete, Working Food Tracker

Food Tracker (React + Express)

Chapter 12 · Capstone: A Complete, Working Food Tracker

Marcus keeps his own household pantry tracked with the app this course just spent eleven chapters building — one Express process, one SQLite file, one React app, all deployed and running for real. What follows is one ordinary session with it.

Step 1 — Scanning a New Item

Marcus scans a carton of milk. Chapter 4's useBarcodeScanner hook decodes the barcode entirely client-side, then handleDetected calls fetch("/api/lookup/...") — Chapter 3's route, which checks barcode_cache first and falls back to a live Open Food Facts call, caching whatever it finds.

Step 2 — Adding It

Chapter 5's AddItemForm pre-fills the name and category from that lookup. Marcus sets the expiry date and submits; the client-side check catches an empty name instantly, but it's Chapter 3's own POST /api/items route — the real gate, by construction — that actually validates and inserts the row, exactly as Chapter 5's finding-box promised it would.

Step 3 — A Few Days Later, an Alert

Chapter 6's alerts route surfaces the milk once it's within three days of its (consistently YYYY-MM-DD-formatted) expiry date — a plain SQL range query needing no index at all, at this app's own realistic scale.

Step 4 — Searching for Something Bought Before

Wanting to check if he's bought a particular brand of yogurt before, Marcus types "yog" into the search box. Chapter 7's useDebouncedSearch waits for him to stop typing, then hits the search route's native, case-insensitive LIKE match — no shadow field required, unlike the Firebase sibling's own nameLower.

Step 5 — Marking the Milk Used

The milk gets finished. Chapter 8's PATCH /api/items/:id/use — never a plain link — sets status='used', stamps used_at, and clears expiry_date to NULL, guarded by AND status = 'active' so accidentally clicking twice changes nothing the second time.

Step 6 — Everything Updates, Without Being Told To Individually

Marking the milk used calls Chapter 10's notifyChange() — one call, with no knowledge of who's listening. The alerts dashboard disappears the milk from its own list; the recipe suggestions (Chapter 9) stop counting it as an expiring ingredient — both react on their own, independently, exactly as Chapter 10's whole redesign was built to make possible.

Step 7 — A Recipe Suggestion

With chicken and spinach both nearing expiry, Chapter 9's Promise.all fan-out queries TheMealDB for both concurrently — not sequentially, a real, honest advantage over the Django sibling's own admitted sequential cost — merging and sorting the results by how many expiring ingredients each recipe actually uses.

Step 8 — All of This, Actually Deployed

Marcus's session happens against a real deployment: Express serving the built React app via Chapter 11's own catch-all SPA route (registered after the API routes, never before), pm2 keeping the process alive, nginx terminating HTTPS in front of it, and the SQLite file sitting on a volume that survives a redeploy.

Chapter Attribution

StepChapter(s) applied
1 — ScanningChapter 4 (camera scanning), Chapter 3 (lookup route + barcode_cache)
2 — Adding the itemChapter 5 (AddItemForm, server-side validation), Chapter 2 (schema, INSERT)
3 — Expiry alertChapter 6 (date range query, YYYY-MM-DD discipline)
4 — SearchChapter 7 (debounce hook, native LIKE)
5 — Marking usedChapter 8 (PATCH route, active-only guard, expiry_date=NULL)
6 — Automatic updatesChapter 10 (ItemsContext, notifyChange, version)
7 — Recipe suggestionChapter 9 (Promise.all fan-out, recipe_cache, relevance sort)
8 — Real deploymentChapter 11 (static serving, SPA fallback, pm2, TLS, persistent volume)
What this whole course was really about
Chapter 1 opened with a single claim: the same language runs on both sides of this app. Every step above is that claim paying off concretely — JSON needing no translation between client and server, Chapter 4's scanning hook plugging into this course's own Express route with a one-line change from its Firebase sibling, Chapter 9's Promise.all turning JavaScript's async-first design into a genuine performance advantage, and Chapter 10's Context-based coordination solving a real cross-feature problem with nothing more than React's own built-in tools. None of these payoffs required abandoning the honest limits named along the way — no ORM, no shadow-field-free search that scales infinitely, no free HTTPS — this course's own throughline was never "JavaScript solves everything," just "the same language, both ends, is a real and specific advantage where it actually applies."
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 — every item belongs to whoever can reach the server, matching the same honest gap named in Food Tracker (Django)'s own capstone, unlike the Firebase sibling course's own Chapter 11, which did implement real per-user Firebase Authentication; Chapter 9's own honest limit on matching generic branded product names against TheMealDB's fixed vocabulary was never solved, only named; and no automated test suite or CI pipeline was covered anywhere in this course.

Hands-On Exercises

Exercise 1

Trace Step 6 in detail: explain exactly what markUsed calls, and how that one call results in both the alerts dashboard and the recipe suggestions updating, without either being called directly.

📄 View solution
Exercise 2

Pick any two steps from Marcus'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 why this course's honest scope note names the same missing multi-user ownership model as Food Tracker (Django)'s own capstone, while explicitly contrasting that against Food Tracker (React + Firebase), which did build real per-user authentication.

📄 View solution

Chapter 12 Quick Reference — Course Complete

  • 8 steps, 11 prior chapters — one continuous, realistic session with the finished, deployed app
  • This course's own throughline, closed out: the same language, both ends, is a real, specific advantage — not a universal solution
  • Honest scope note: no meal planner, no offline/PWA, no multi-user ownership model (same gap as the Django sibling), TheMealDB matching remains best-effort, no automated tests/CI
  • Food Tracker (React + Express) is now complete — 12/12 chapters