Lab: Persist everything
Wire the whole loop to the real database: schema + seed, four routes built and tested at the door, screens swapped off mock data, and the refresh test passed.
The biggest lab of the course — by the end, Replyable is a real app with a memory: messages live in SQLite, screens read through the API, replies and status changes persist across restarts. Take it in the numbered order; each step leaves the app working — which means the commits between steps are natural stopping points. Plan two or three evenings, and stop at commits, not mid-step.
- 1Set up the database: ask the assistant to add
better-sqlite3, createlib/db.ts(connection + create-tables-if-missing from the schema lesson — adapt column names to your variant), and alib/seed.tsthat inserts your five mock messages into the database if it's empty. Run the app, open the DB viewer, see your rows. Commit:db + seed. - 2Build the two GET routes and test them before touching any screen — visit
/api/messagesand/api/messages/1directly in the browser and read the JSON. Testing doors before rooms is the habit that localizes every future bug to one side of the API line. Commit. - 3Swap the Inbox and Thread off mock data: the screens now fetch from the routes (the assistant will use a data-fetching pattern — have it explain loading state in one paragraph, because you now have one: the moment between asking and answer, which needs a spinner or skeleton). The satisfying part: your components barely change — props are props, exactly as promised. Delete
mock-data.tswith ceremony. Commit. - 4Build the two write routes + wire the actions: PATCH for status (Send reply also flips status to
replied; a Done button flipsdone), POST for replies (with door validation on all inputs — test the door directly with a bad payload and watch it refuse; the address bar only knocks with GET, so ask the assistant for acurlcommand, or paste afetch('/api/replies', { method: 'POST', ... })into the browser Console — the Console can knock on doors the address bar can't, and this becomes your standard write-route test). After wiring: send a reply in the UI, watch it appear, check the row in the DB viewer. Commit. - 5The refresh test, then the restart test: reply to a message, refresh the browser — everything holds. Stop the dev server entirely, restart, everything still holds. That un-event is the whole module's deliverable. Add one 'paste in a new message' form (your v1 stand-in for email integration — it's on the cut list, remember) so the app can actually receive new work. Final commit:
v1 skeleton walks.
Door-testing reps: hit each route with three malformed requests and verify the refusals (wrong status value, empty body, nonexistent id); trace one full click-to-database round trip in writing ('click → fetch → route → SQL → JSON → state → redraw'); and diagnose two planted bugs from symptoms alone — 'saves but doesn't show' vs. 'shows but doesn't save' — naming which side of the API line each lives on before you look. You now think in three places; the problem set proves it.