Expiry Alerts
Food Tracker (React + Firebase)
Chapter 7 · Expiry Alerts
Chapter 6 finally protected real data with real rules. This chapter is the first payoff of having that data at all: surfacing which items are about to go to waste.
The On-Demand Query
Filtering on status == "active" is technically redundant with Chapter 2's own "used items omit expiryDate entirely" design — a range comparison already excludes documents missing the field. It's included anyway as explicit, readable intent rather than relying on an implicit side effect of the data model to do the filtering silently.
The Composite Index Requirement
Run the query above for the first time, and Firestore throws an error rather than executing it — something like "The query requires an index." This isn't a mistake in the query; it's Firestore telling you the truth about how it works. A single-field query gets an automatic index for free. A query combining an equality filter on one field (status) with a range filter on a different field (expiryDate) needs a composite index, declared ahead of time — either by clicking the link Firestore's own error message provides (which pre-fills the exact index needed), or by committing a firestore.indexes.json file to source control so the same index gets created automatically in every environment.
SQL handles the equivalent query differently: an unindexed multi-column WHERE clause still runs — just slower, via a full table scan, with no error at all. Firestore refuses outright rather than running a query it can't serve efficiently at scale. Neither behavior is objectively better; they reflect two different philosophies about whether "it'll just be slow" is an acceptable default.
Rendering the Dashboard
-
{expiring.map(item =>
- {item.name} — expires {item.expiryDate.toDate().toLocaleDateString()} )}
Should Alerts Be Proactive? An Optional Scheduled Function
The dashboard above only tells anyone anything if they actually open the app. A genuinely proactive alert — a notification that arrives even when nobody's looking — needs code running on a schedule, independent of any user visiting the page. Firebase's onSchedule lets a Cloud Function run on a cron-style schedule with no server kept alive between runs:
This is explicitly optional — a stretch feature, not the MVP. The on-demand dashboard query above is what this course actually requires; a scheduled proactive notification is a genuine enhancement worth attempting once the core app works, not a prerequisite for it.
What the Other Three Courses Would Need Instead
| Course | How it would run a daily proactive check |
|---|---|
| Food Tracker (FastAPI) | A scheduler library (e.g. APScheduler) running inside a continuously-alive server process |
| Food Tracker (Django) | Celery Beat, django-crontab, or an OS-level cron job, all requiring something to stay running |
| Food Tracker (React + Express) | node-cron or a system crontab entry, again requiring a persistently-running process |
| Food Tracker (React + Firebase) | onSchedule — no process kept running between invocations at all |
Where This Course Is Headed
Item history with live search (and Firestore's own honest limits on text search), marking items used, recipe lookup, Firebase Authentication, deployment, and a capstone.
Hands-On Exercises
Explain what a Firestore composite index actually is, why this chapter's own query needs one, and how SQL handles the equivalent multi-column query differently.
📄 View solutionExplain the difference between the on-demand dashboard query and the optional scheduled Cloud Function. What real problem does the scheduled function solve that the on-demand query, by itself, never can?
📄 View solutionExplain the finding-box's claim that this course applies its "no server you deploy yourself" idea to time itself. What would each of the three sibling courses need to keep running just to check expiry dates once a day, and why doesn't this course need the equivalent?
📄 View solutionChapter 7 Quick Reference
- On-demand query —
where("status","==","active")+where("expiryDate","<=",threshold), required for this course - Composite index — required for multi-field filter combinations; Firestore refuses to run the query until one exists, unlike SQL's slower-but-runs approach
onSchedule— a scheduled Cloud Function, optional/stretch, for proactive daily checks- No persistent process needed — unlike the cron/Celery/node-cron setup every sibling course would require
- This chapter's own throughline: "no server you deploy yourself" applies to scheduled/background work too, not just request-driven code
- Next chapter: Item History & Live Search-as-You-Type