Backend route and lib sweep: all supabase-js call sites replaced with Drizzle

juanjo converts every remaining supabase-js consumer on the backend - four lib modules and seven route files - to Drizzle ORM in 16 commits. Two runtime bugs introduced by the sweep are fixed afterward: an SSE headers-already-sent crash and a column-name casing mismatch in the tabular context builder.

infrastructure

The lib layer goes first. backend/src/lib/users.ts is new: listAllUsers() returns { id, email, name }[] from the users table as a replacement for auth.admin.listUsers. It's used by the sharing features in three routes. The dead Supabase admin client factory in workflows.ts is deleted immediately after.

access.ts, userSettings.ts, documentVersions.ts, and chatTools.ts follow. All four previously accepted a db: Supa parameter so callers could pass in a client instance; after the rewrite they import the module-level Drizzle db directly and the parameter becomes _db?: unknown. Call sites in routes stop passing db entirely.

Route migrations are the bulk of the diff: downloads.ts, projectChat.ts, chat.ts, projects.ts (+799/-499), documents.ts (+927/-639), workflows.ts, and tabular.ts (+896/-698). Every route follows the same structure: import Drizzle tables and operators at the top, add *ToWire() mapper functions that translate camelCase Drizzle properties back to snake_case JSON keys, wrap each handler in try/catch. chat.ts gets explicit chatToWire() and messageToWire() helpers. projects.ts and tabular.ts are the largest files - they handle sharing, member lookup, folder structures, and complex filtering.

ed0cc46 patches an outer-catch crash in projectChat.ts: after an SSE response starts streaming, res.status(500).json() throws because headers are already sent. The fix checks res.headersSent before attempting the error response. bdb80c4 fixes buildTabularContext reading a Drizzle camelCase property (documentId) with the old snake_case key (document_id), which returned undefined silently.

So what Worth importing as a unit if your fork needs the same Drizzle migration. The `*ToWire()` mapper pattern is the reusable idea - it kept the frontend JSON contract stable through the entire ORM swap with no frontend changes. Test SSE paths and any code that destructures DB row keys before shipping; those are where post-migration bugs hide.

View this fork on GitHub →

Spotted something wrong? Or know the PR text has fresher detail than the writeup above?

Commits in this thread

16 commits from juanjo/mike, oldest first. Source extracted verbatim from the harvested git log.

SHA Subject Author Date
68d18876 feat(backend): replace Supabase auth.admin.listUsers with Drizzle helper Juan Vidal 2026-05-08 ↗ GitHub
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
cfc84fbf chore(backend): remove dead Supabase admin client from workflows.ts Juan Vidal 2026-05-08 ↗ GitHub
36c889ce feat(backend): migrate lib/access.ts to Drizzle Juan Vidal 2026-05-08 ↗ GitHub
a41622ab feat(backend): migrate lib/userSettings.ts to Drizzle Juan Vidal 2026-05-08 ↗ GitHub
3a5c8b6d feat(backend): migrate lib/documentVersions.ts to Drizzle Juan Vidal 2026-05-08 ↗ GitHub
d352d15c feat(backend): migrate lib/chatTools.ts to Drizzle Juan Vidal 2026-05-08 ↗ GitHub
ff63879b feat(backend): migrate downloads.ts to Drizzle Juan Vidal 2026-05-08 ↗ GitHub
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
d2f30d55 feat(backend): migrate projectChat.ts to Drizzle Juan Vidal 2026-05-08 ↗ GitHub
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
318e5594 feat(backend): migrate chat.ts to Drizzle Juan Vidal 2026-05-08 ↗ GitHub
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ed0cc463 fix(projectChat): guard outer catch against post-headers-sent SSE crash Juan Vidal 2026-05-08 ↗ GitHub
5e802ac6 feat(backend): migrate projects route to Drizzle Juan Vidal 2026-05-08 ↗ GitHub
commit body
Replace all supabase-js calls in backend/src/routes/projects.ts with
Drizzle ORM equivalents, add projectToWire/docToWire/folderToWire helpers
for snake_case wire shape preservation, and wrap every async handler in
try/catch. Drop the redundant _db shim arg from all checkProjectAccess
call sites. The listAllUsers() usage from P2-1 is preserved intact.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3e8d1813 feat(backend): migrate documents route to Drizzle Juan Vidal 2026-05-08 ↗ GitHub
commit body
Replace all 30 supabase-js calls in documents.ts with Drizzle ORM queries.
Drop _db shim args from lib calls; preserve snake_case wire shapes via
inline mappers; wrap all handlers in try/catch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
b691f92c feat(backend): migrate workflows route to Drizzle Juan Vidal 2026-05-08 ↗ GitHub
commit body
Replace all 18 supabase-js calls in workflows.ts with Drizzle ORM queries.
Add workflowToWire mapper for snake_case wire shape; drop _db shim args;
preserve listAllUsers() from P2-1; wrap all handlers in try/catch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
7cf1e82e fix(backend): workflow list ordering + tighten Drizzle update types Juan Vidal 2026-05-08 ↗ GitHub
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
022434f9 feat(backend): migrate tabular route to Drizzle Juan Vidal 2026-05-08 ↗ GitHub
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
bdb80c46 fix(tabular): correct snake_case→camelCase mismatch in buildTabularContext Juan Vidal 2026-05-08 ↗ GitHub

Capture this thread into my fork

Download a single Markdown prompt that tells Claude how to port every commit above into your working tree — adapting paths and structure to match your repo. Run it via claude -p < capture-thread-183.md from inside the repo you want the changes in.

⬇ Download capture-thread-183.md