Marking Items Used
Food Tracker (React + Express)
Chapter 8 · Marking Items Used
Every earlier chapter built toward this exact moment: an item is finally used up, and the row created back in Chapter 5 needs to reflect that — without ever disappearing from the history Chapter 7 searches.
The Route
Two things worth noticing in that single query. First, expiry_date = NULL — the same nullable-not-deleted design Chapter 2 committed to from the start, now paying off: the row stays in the table forever, but its expiry date genuinely goes away, exactly the way Chapter 6's own alerts query (expiry_date IS NOT NULL) already expects. Second, AND status = 'active' in the WHERE clause — the update only actually touches a row that's currently active, which means marking an already-used item "used" again is a no-op rather than silently re-stamping used_at with a new, incorrect timestamp.
GET — a browser's own link-prefetching, a crawler following every link on a page, or simply a user middle-clicking to open in a new tab could all trigger a GET request without the user ever intending to mark anything used. PATCH (or POST) requires the request to come from an explicit action — a button's onClick firing a real fetch call — never something a browser might do on its own while merely loading or navigating a page.
The React Action
refresh from useExpiryAlerts specifically for this moment, rather than only fetching once internally. Calling refresh() right after the PATCH succeeds re-runs the alerts query, and since the item's status is now 'used', it no longer matches that query's own WHERE status = 'active' condition — it disappears from the dashboard immediately, with no full page reload and no manual list-filtering logic on the frontend at all. The backend query is the single source of truth for "what's currently expiring soon"; the frontend just asks it again.
PATCH to actually succeed, then re-fetch — accepting a small, honest delay in exchange for never showing the user a state the server hasn't actually confirmed.
Where This Course Is Headed
Recipe lookup with TheMealDB next — an Express route and a React results component, matching items nearing expiry against real recipes.
Hands-On Exercises
Explain what the AND status = 'active' clause in the UPDATE statement actually prevents, and what would go wrong without it if a user managed to click "Mark Used" twice on the same item.
📄 View solutionExplain why marking an item used must never be implemented as a plain GET request, with a concrete example of how a GET-based version could be triggered unintentionally.
📄 View solutionExplain how calling refresh() after a successful markUsed request causes the item to disappear from the dashboard, tracing exactly which earlier chapter's own query condition makes that work.
📄 View solutionChapter 8 Quick Reference
- Route: PATCH /api/items/:id/use — sets status='used', used_at, and clears expiry_date to NULL
- Guard: AND status = 'active' in the WHERE clause makes re-marking an already-used item a safe no-op
- Never a GET: a state-changing action must require an explicit request, not something a browser could trigger while merely loading a page
- The payoff: refresh() from Chapter 6 re-runs the alerts query, which naturally excludes the now-used item — no manual frontend filtering needed
- Deliberate simplicity: wait-then-refresh, not optimistic UI updates
- Next chapter: Recipe Lookup with TheMealDB