Capstone: A Complete, Working Food Tracker
Food Tracker (React + Firebase)
Chapter 13 · Capstone: A Complete, Working Food Tracker
Maya has just installed the finished app on her phone, at its real deployed URL. Every step below is a real feature, built in a specific earlier chapter, used exactly the way it was designed to be used.
Maya creates an account with her email and a password. From this moment on, her pantry is genuinely hers — not merely hidden behind a login screen, but enforced by the Security Rules built across Chapters 6, 9, and 11: every read, write, and update on her items now checks request.auth.uid == resource.data.userId, on Firebase's own servers, regardless of what the app's UI does or doesn't show.
She points her phone's camera at a yogurt carton's barcode. The getUserMedia-based scanning component (Chapter 4) decodes it and hands it to lookupBarcode (Chapter 3), which checks barcodeCache first, finds nothing (a genuinely new product), queries Open Food Facts, and caches the result for the next person who scans the same barcode. She confirms the name, picks an expiry date, and saves — Chapter 5's addItem, now including her own userId per Chapter 11.
Behind the scenes, the real document reflects every data-modeling decision from Chapter 2: name and nameLower, barcode, category, status: "active", a genuine Firestore Timestamp for expiryDate, addedAt set by serverTimestamp() (and enforced by Chapter 6's own rule requiring it), and userId. Nothing here was left to chance — every field exists because an earlier chapter deliberately decided it should.
Opening the app later that week, the dashboard flags the yogurt as expiring soon — Chapter 7's own query, running cleanly now that the composite index it needs was codified in firestore.indexes.json and deployed for real in Chapter 12, rather than existing only as a console click-through on one developer's machine.
Wanting to buy more of something she'd tracked before, Maya types "yogurt" into search. This finds "Greek Yogurt" — a genuine mid-word match, something the raw Firestore prefix trick Chapter 8 first demonstrated could never do. It works because Chapter 8's actual chosen implementation filters the loaded history client-side with .includes(), not the more limited prefix-only Firestore query — the more capable of the chapter's own two approaches, deliberately chosen over the more constrained one.
She finishes it. One tap calls Chapter 9's markItemUsed: status flips to "used", usedAt is stamped, and expiryDate is genuinely removed via deleteField() — not nulled. The item drops off the active dashboard immediately, but per Chapter 2's original design, it never disappears from her history at all.
With chicken and eggs both nearing expiry, Maya opens the recipe tab. Chapter 10's suggestRecipes fans out one TheMealDB query per ingredient, merges the results, and sorts by how many of her own expiring items each recipe actually uses — a recipe using both chicken and eggs together surfaces above one using only either alone.
Every step above happened at the app's actual Firebase Hosting URL, over HTTPS by default — the exact requirement Chapter 4 flagged early and left as an assumption, now genuinely satisfied since Chapter 12's deployment.
Chapter Attribution
| Step | Chapter(s) applied |
|---|---|
| 1 — Signing up | Chapter 11 (Firebase Authentication), Chapters 6 & 9 (rules extended by ownership) |
| 2 — First scan | Chapter 4 (camera scanning), Chapter 3 (barcode lookup + cache), Chapter 5 (add-item flow), Chapter 11 (userId) |
| 3 — What got stored | Chapter 2 (data model), Chapter 6 (serverTimestamp enforcement) |
| 4 — Expiry alert | Chapter 7 (expiry query), Chapter 12 (deployed composite index) |
| 5 — Search | Chapter 8 (item history & search) |
| 6 — Marking used | Chapter 9 (deleteField, the used-item invariant) |
| 7 — Recipe suggestion | Chapter 10 (TheMealDB fan-out and merge) |
| 8 — Real deployed URL | Chapter 12 (Hosting, HTTPS), Chapter 1 (the architecture this all rests on), Chapter 4 (the requirement finally met) |
Hands-On Exercises
In Step 5, Maya's search for "yogurt" finds "Greek Yogurt" — a mid-word match. Explain specifically why this works, referencing which of Chapter 8's two approaches actually powers the real search feature.
📄 View solutionPick any two steps from Maya's session and explain how each one depends on at least two earlier chapters working together, not just one chapter in isolation.
📄 View solutionExplain why the honest scope note excludes push notifications specifically, even though Chapter 7 already built a scheduled Cloud Function that checks for expiring items.
📄 View solutionChapter 13 Quick Reference — Course Complete
- 8 steps, 12 prior chapters — one continuous, realistic session with the finished, deployed app
- This course's own throughline, closed out: no server the developer writes, runs, or deploys themselves — genuinely true across every single feature built
- Honest scope note: no meal planner, no offline/PWA support, no shared-household pantries, no push-notification delivery, no automated tests/CI
- Food Tracker (React + Firebase) is now complete — 13/13 chapters