WI-017: Mobile search against the API
WI-017: Mobile search against the API
Problem
The scaffold rendered five hard-coded games because it had nowhere to ask. WI-012 gave it somewhere.
Its spec said the sample data should be deleted the moment that happened. This deletes it.
In scope
src/api.ts— a typed client that validates responses against the shared contract.src/api-host.ts— device-side host resolution, kept separate for a reason below.- The list screen becomes a live search: debounced, cancellable, with loading, empty and error states.
- The detail screen loads a real game.
src/sample-games.tsdeleted.
Out of scope
- Anything requiring a signed-in user — WI-013.
- Caching, offline, retry-with-backoff. This talks to a dev server on a laptop; inventing a sync layer before there is an account is building for an imagined product.
Two things worth writing down
The API client imports nothing from Expo or React Native. expo-constants pulls in React Native’s Flow-typed entry point, and anything importing that becomes unparseable by vitest — the test suite failed with “Flow is not supported” pointing at react-native/index.js. Host detection moved to api-host.ts, which the root layout calls once; the client is told the answer. The client is now pure and testable.
Responses are validated, not trusted. The API and this app ship together today. They will not always: a phone keeps whatever build its owner last installed, so “client and server always agree” stops being true the first time somebody declines an update. A response that does not match the contract throws with a readable message rather than rendering undefined into a list.
Definition of done
- Searching by title, designer, mechanic and theme returns real games.
- A typo still finds the game — the trigram path reaches the screen.
- Requests are debounced, and an in-flight request is aborted when the query changes, so a slow response for “cat” cannot overwrite a newer one for “catan”.
- A blank query does not call the API at all — it would be a 400, and that is not the user’s mistake.
- An unreachable API produces a readable message, not “Network request failed”.
- A response that violates the contract throws rather than rendering undefined.
- A slug is validated with
slugSchemafrompackages/sharedbefore a request is made. -
src/sample-games.tsno longer exists. - The client is proven against the live Worker, not only against a stub.
- Gate green.
Verification
pnpm --filter @tabletop/db run db:up && pnpm --filter @tabletop/db run seedpnpm --filter @tabletop/api dev # terminal onepnpm --filter @tabletop/mobile dev # terminal two, then press wOn a physical device, api-host.ts derives the laptop’s LAN address from Expo’s own hostUri, because localhost on a phone is the phone. EXPO_PUBLIC_API_URL overrides it.