Item History & Live Search-as-You-Type
Food Tracker (FastAPI)
Chapter 7 · Item History & Live Search-as-You-Type
Every item ever added stays in the items table forever — Chapter 2's own design, active or used. This chapter surfaces that whole history, searchable in real time as the user types.
The Search Route
Deliberately no status filter, exactly like every sibling course's own search route — this searches the entire history, active and used items both, since re-adding something bought before is exactly the case this endpoint exists for.
.ilike() is SQLAlchemy's own built-in, explicitly-named case-insensitive LIKE — no separate lowercase field to maintain, the same real advantage Food Tracker (Django)'s icontains lookup and Food Tracker (React + Express)'s own raw SQLite LIKE both already claimed for themselves. All three of this quartet's SQL-backed courses get native, case-insensitive substring search essentially for free, each expressed in that framework's own idiom — ilike() here, icontains in Django, a plain LIKE in Express. Only Food Tracker (React + Firebase) needed a workaround at all, maintaining its own nameLower shadow field specifically because Firestore has no equivalent native capability. This isn't really a FastAPI-specific advantage — it's a relational-database advantage the three SQL courses in this quartet all happen to share, each one naming it in its own equivalent chapter.
A Debounce, Written as Plain Functions
The same underlying idea as Food Tracker (React + Express)'s own useDebouncedSearch hook, here with no hook at all — just setTimeout/clearTimeout and a module-level variable:
clearTimeout(searchTimeoutId) at the start of onSearchInput cancels whatever timer the previous keystroke scheduled, before scheduling a new one — the same underlying discipline as Chapter 4's stopScanner() calls, applied here to a timer instead of a camera stream.
clearTimeout here would mean an extra, unnecessary search request firing occasionally — a wasted network call, nothing more. Chapter 4's own stopScanner() omission would leave a camera physically running in the background indefinitely. Both are the same underlying "clean up what the last run scheduled" pattern, but the actual cost of getting it wrong is meaningfully different — worth knowing which mistakes in a framework-free app are merely wasteful versus genuinely harmful.
Where This Course Is Headed
Marking items used next — a PATCH endpoint transitioning status and clearing the expiry date without deleting the row.
Hands-On Exercises
Explain why this chapter says the native case-insensitive substring search advantage isn't really FastAPI-specific, naming which three courses in the quartet share it and which one doesn't.
📄 View solutionExplain why clearTimeout(searchTimeoutId) is described as the same underlying pattern as Chapter 4's stopScanner() calls, and why the two are still described as having meaningfully different stakes if forgotten.
📄 View solutionExplain why the search route deliberately has no status filter, unlike Chapter 6's own alerts route.
📄 View solutionChapter 7 Quick Reference
- Route: GET /search?q=... — ilike('%'+q+'%'), no status filter, limited to 50 results
- Quartet-wide finding: Django's icontains, Express's LIKE, and this course's ilike() all share the same native advantage — only the Firebase sibling needed a nameLower workaround
- Debounce: plain setTimeout/clearTimeout, the same underlying pattern as Chapter 4's stopScanner(), but lower stakes if forgotten
- Honest limit: LIMIT 50 is a scope decision, not real pagination
- Next chapter: Marking Items Used