---
phase: 14-postgresql-migration
plan: 04
type: execute
wave: 2
depends_on: [14-01]
files_modified:
- src/server/routes/items.ts
- src/server/routes/categories.ts
- src/server/routes/threads.ts
- src/server/routes/setups.ts
- src/server/routes/auth.ts
- src/server/routes/oauth.ts
- src/server/routes/images.ts
- src/server/routes/settings.ts
- src/server/routes/totals.ts
- src/server/middleware/auth.ts
autonomous: true
requirements: [DB-01, DB-02]
must_haves:
truths:
- "Every route handler awaits service function calls"
- "All route handlers that call services are async"
- "No route returns a Promise object instead of resolved data"
- "Auth middleware awaits all DB queries for session and API key validation"
artifacts:
- path: "src/server/routes/items.ts"
provides: "Async item route handlers"
contains: "await"
- path: "src/server/routes/settings.ts"
provides: "Async settings handlers with direct DB calls"
contains: "await"
- path: "src/server/middleware/auth.ts"
provides: "Async auth middleware with awaited DB lookups"
contains: "await"
key_links:
- from: "src/server/routes/*.ts"
to: "src/server/services/*.ts"
via: "await service function calls"
pattern: "await .*(get|create|update|delete)"
- from: "src/server/middleware/auth.ts"
to: "src/db/schema.ts"
via: "session and API key DB queries"
pattern: "await.*db\\.select"
---
Convert all 9 route handler files and the auth middleware to properly await async service calls and DB operations. Route handlers that call service functions must be async and await the results.
Purpose: With services now async (Plan 03), route handlers must await them. Missing awaits would return Promise objects as JSON responses instead of actual data. The auth middleware queries sessions and API keys on every request -- these direct DB calls must also be async.
Output: All route files and auth middleware properly await service/DB calls.
@$HOME/.claude/get-shit-done/workflows/execute-plan.md
@$HOME/.claude/get-shit-done/templates/summary.md
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/phases/14-postgresql-migration/14-CONTEXT.md
@.planning/phases/14-postgresql-migration/14-RESEARCH.md
@.planning/phases/14-postgresql-migration/14-03-SUMMARY.md
@src/server/routes/items.ts
@src/server/routes/settings.ts
@src/server/middleware/auth.ts
Conversion rules for routes:
- Handler callback must be `async (c) => { ... }`
- Every service call: `const result = serviceFunction(db, ...)` -> `const result = await serviceFunction(db, ...)`
- Settings route has direct DB calls: add `await` and remove `.all()/.get()/.run()`
- OAuth routes may have direct DB calls for token validation
Conversion rules for auth middleware:
- Middleware function must be async
- Session lookup: `db.select()...where(eq(sessions.id, ...))` -> add `await`, remove `.get()`, use destructuring
- API key lookup: same pattern
Task 1: Convert data route handlers to async (items, categories, threads, setups, totals)
src/server/routes/items.ts, src/server/routes/categories.ts, src/server/routes/threads.ts, src/server/routes/setups.ts, src/server/routes/totals.ts
src/server/routes/items.ts, src/server/routes/categories.ts, src/server/routes/threads.ts, src/server/routes/setups.ts, src/server/routes/totals.ts
For each route file, read the full file first. Then:
1. Ensure every handler callback is `async (c) => { ... }` (many may already be async for body parsing)
2. Add `await` before every service function call
3. If any handler has direct DB calls (`.select()`, `.insert()`, etc.), apply the same rules as services: remove `.all()/.get()/.run()`, use destructuring for single rows
**items.ts** -- Handlers call: `getAllItems(db)`, `getItemById(db, id)`, `createItem(db, data)`, `updateItem(db, id, data)`, `duplicateItem(db, id)`, `deleteItem(db, id)`. Add `await` before each.
**categories.ts** -- Handlers call: `getAllCategories(db)`, `createCategory(db, data)`, `updateCategory(db, id, data)`, `deleteCategory(db, id)`. Add `await` before each.
**threads.ts** -- Handlers call: `getAllThreads(db)`, `getThreadById(db, id)`, `createThread(db, data)`, `updateThread(db, id, data)`, `deleteThread(db, id)`, `resolveThread(db, id, data)`, `unresolveThread(db, id)`, `addCandidate(db, data)`, `updateCandidate(db, id, data)`, `removeCandidate(db, id)`, `reorderCandidates(db, data)`. Add `await` before each.
**setups.ts** -- Handlers call: `getAllSetups(db)`, `getSetupById(db, id)`, `createSetup(db, data)`, `updateSetup(db, id, data)`, `deleteSetup(db, id)`, `updateSetupItems(db, id, data)`, `updateClassification(...)`. Add `await` before each.
**totals.ts** -- Handlers call totals service functions. Add `await` before each.
! grep -n "= getAllItems\|= getItemById\|= createItem\|= getAllCategories\|= getAllThreads\|= getAllSetups" src/server/routes/items.ts src/server/routes/categories.ts src/server/routes/threads.ts src/server/routes/setups.ts 2>/dev/null | grep -v "await" && echo "PASS" || echo "FAIL"
- items.ts: every service call is preceded by `await`
- categories.ts: every service call is preceded by `await`
- threads.ts: every service call is preceded by `await`
- setups.ts: every service call is preceded by `await`
- totals.ts: every service call is preceded by `await`
- No route handler assigns a service call result without `await`
All data route handlers properly await async service calls.
Task 2: Convert auth, OAuth, settings, images routes and auth middleware to async
src/server/routes/auth.ts, src/server/routes/oauth.ts, src/server/routes/settings.ts, src/server/routes/images.ts, src/server/middleware/auth.ts
src/server/routes/auth.ts, src/server/routes/oauth.ts, src/server/routes/settings.ts, src/server/routes/images.ts, src/server/middleware/auth.ts
**auth.ts** -- Handlers call auth service functions. Add `await` before each service call.
**oauth.ts** -- Handlers call OAuth service functions. Add `await` before each service call. Also check for any direct DB queries in OAuth routes and apply async conversion.
**settings.ts** -- This route likely accesses the database DIRECTLY (no service layer) using `db.select().from(settings)` etc. Apply full async conversion:
- Remove `.all()` -- `const rows = await db.select().from(settings)`
- Remove `.get()` -- `const [row] = await db.select().from(settings).where(...)`
- Remove `.run()` -- `await db.insert(settings).values(...)`
**images.ts** -- May call image service functions. Add `await` before each service call.
**src/server/middleware/auth.ts** -- The auth middleware queries sessions and API keys on every authenticated request. These are direct DB calls that must become async:
- Make the middleware function async (if not already)
- Add `await` before all DB queries (session lookup, API key lookup)
- Remove `.get()` -> use destructuring: `const [session] = await db.select()...`
- Remove `.all()` if present
- This is critical -- the auth middleware runs on every POST/PUT/DELETE request, so missing awaits here would break ALL write operations
**After all conversions, run a PGlite smoke test to verify routes work end-to-end:**
```bash
bun -e "
import { createTestDb } from './tests/helpers/db.ts';
import * as schema from './src/db/schema.ts';
const db = await createTestDb();
// Verify auth middleware can be imported without errors
const authMod = await import('./src/server/middleware/auth.ts');
console.log('Auth middleware imports OK');
// Verify settings route pattern works
const rows = await db.select().from(schema.settings);
console.log('Direct DB query works, settings count:', rows.length);
console.log('Route smoke test PASSED');
"
```
! grep -n "\.all()\|\.get()\|\.run()" src/server/routes/settings.ts src/server/routes/auth.ts src/server/routes/oauth.ts src/server/routes/images.ts src/server/middleware/auth.ts 2>/dev/null && bun run lint 2>&1 | tail -3 && echo "PASS" || echo "FAIL"
- auth.ts: every service call is preceded by `await`
- oauth.ts: every service call is preceded by `await`
- settings.ts: does NOT contain `.all()`, `.get()`, or `.run()`
- settings.ts: contains `await db.select()` and `await db.insert()`
- images.ts: every service call is preceded by `await`
- src/server/middleware/auth.ts: does NOT contain `.get()` or `.all()` on DB calls
- src/server/middleware/auth.ts: contains `await` before all DB select queries
- All files pass lint
Auth, OAuth, settings, and images routes properly await all DB operations. Auth middleware fully converted to async DB operations. Lint passes.
- `grep -rn "\.all()\|\.get()\|\.run()" src/server/routes/ src/server/middleware/auth.ts` returns NO matches
- Every route handler that calls a service function uses `await`
- Auth middleware awaits all DB queries
- `bun run lint` passes
All 9 route files and auth middleware await async service/DB calls. Settings route uses async direct DB calls. Auth middleware properly awaits session and API key lookups. No route handler will return a Promise object instead of resolved data. Lint passes.