Part 1
Requests in Postgres
Study first
- The MCP server quickstart for TypeScript: tools, resources, and stdio as the transport Claude Code uses for a local server.
- postgres.js and PGlite. PGlite is Postgres compiled to WebAssembly, so the SQL in this project runs on both.
Build
- A local Postgres: on macOS,
brew install postgresql@18,brew services start postgresql@18,createdb workbot; on Linux, the distribution's package.DATABASE_URL=postgres://localhost/workbotin.env.local. src/lib/db.ts: onequery(text, params)function that runs one statement with$1to$nparameters and returns the rows as objects. With apostgres://URL it uses postgres.js; without one it opens an embedded PGlite database in.data/pg, andmemory://for tests. The schema, a list of idempotent statements insrc/lib/schema.ts, runs at startup.- Split the request model: schemas, constants and labels in
src/lib/request-model.ts, which client components may import; the queries stay insrc/lib/requests.ts. - Rewrite
createRequest,listRequests,getRequestandupdateRequestas SQL, and parse every row through theWorkRequestschema. /api/healthrunsselect 1and answers 503 when the database is unreachable.error.tsxandnot-found.tsxgive the app an error boundary and a 404 page.
Done when
pnpm testpasses on the in-memory database, a request filed in the app is still there afterpnpm devrestarts, and/api/healthreports the database.

Technical notes
- Route bundles do not share module instances, so the connection is kept on
globalThis, as the in-memory list was. An embedded database can only be open in one process. created_at as "createdAt"in the select list, so rows arrive in the shape the schema expects and no mapping code exists.- A
uuidcolumn with a default: an id that is not a UUID is answered as not found before the query runs. - Each schema statement is idempotent,
create table if not existsand the like, so the list can run on every start against any earlier state. An earlier statement is never edited, because databases that already ran it would not see the edit; a change is a new statement at the end. - The health test runs against the in-memory database, so it checks the report's shape. The server's reachability is checked by opening
/api/healthafter a deploy.
