Plan 2: entire backend ported to Drizzle, Supabase packages removed
juanjo finishes the mechanical backend migration: 16 commits convert every remaining supabase-js call site to Drizzle ORM across all route files and lib helpers. Two post-migration bug fixes suggest the sweep was large enough to introduce subtle runtime errors.
The migration starts in the lib layer. A new backend/src/lib/users.ts (68d1887) exposes listAllUsers() and emailToUserIdMap() to replace auth.admin.listUsers. The function is used in three routes that need to resolve emails to user IDs for sharing features. The dead Supabase admin client in workflows.ts is removed in a follow-up (cfc84fb).
Then four lib modules: access.ts (36c889c), userSettings.ts (a41622a), documentVersions.ts (3a5c8b6), chatTools.ts (d352d15). The chatTools.ts rewrite is 479 lines changed - it's the largest single file. All four drop the db: Supa parameter that previously threaded a supabase client through every function call; they now import the module-level Drizzle db directly. Call sites in routes no longer need to pass db at all.
Route files follow: downloads.ts (ff63879), projectChat.ts (d2f30d5), chat.ts (318e559), projects.ts (5e802ac, +799/-499), documents.ts (3e8d181, +927/-639), workflows.ts (b691f92), tabular.ts (022434f, +896/-698). The consistent pattern across all of them: a set of *ToWire() mapper functions (e.g., chatToWire, projectToWire, docToWire) translate Drizzle's camelCase property names back to the snake_case JSON keys the frontend already expects. Every handler gets wrapped in try/catch since Drizzle throws on error rather than returning { data, error }.
Two fixes landed after the sweep. ed0cc46 guards the outer catch in projectChat.ts against crashing when the SSE response headers have already been sent - the outer try/catch was calling res.status(500).json() after streaming had begun, which Node rejects. bdb80c4 fixes a snake_case key being read with a camelCase property name in buildTabularContext, introduced when the column name mapping wasn't applied consistently.
Spotted something wrong? Or know the PR text has fresher detail than the writeup above?