Backend Clerk verification + Supabase data layer
From the PR description
Summary
Completes the Clerk migration on the backend and consolidates the data layer onto Supabase (database and file storage), so Gary can run without Cloudflare. After this PR, once the backend is deployed, the app works end to end.
Follows PR #6 (frontend Clerk auth). Scope confirmed with the user: backend Clerk verification + schema fix + Supabase Storage.
What changed
Backend authentication
requireAuthnow verifies Clerk session JWTs via@clerk/backend(verifyToken) instead of Supabase tokens. The Clerk user id flows tores.locals.userId.- Clerk session tokens don't carry email, so it's resolved through the Clerk API and cached (5 min). Non-critical - an empty email just yields no shared items.
- The sharing/"people" endpoints (
projects,tabular,workflows) now look users up via Clerk (getClerkUserEmail/listClerkUsers) instead ofsupabase.auth.admin. - Removed the unused Supabase JWT helper (
getUserIdFromRequest).
Database schema (backend/schema.sql)
user_profiles.user_idanduser_api_keys.user_idchanged fromuuid references auth.users(id)to plaintext- Clerk user ids (user_xxx) aren't UUIDs. The other tables already useduser_id text.- Dropped the
on_auth_user_createdSupabase Auth trigger; the backend already creates profiles lazily (ensureProfileRow). - Schema no longer references
auth.usersat all.
Account deletion
DELETE /user/accountdeletes the Clerk user + the profile/api-key rows (the previous code called the Supabase Auth admin API).
Storage → Supabase Storage
- Storage client switched from R2-specific
R2_*env vars to genericS3_*vars with a configurableS3_REGION. Supabase Storage is S3-compatible, so the existing@aws-sdk/client-s3code works against it. - Removed the dead, unused
frontend/src/lib/storage.ts.
Docs / env
- New
docs/SUPABASE_SETUP.md- step-by-step: create the Supabase project, run the schema, set up the storage bucket + S3 keys, configure and deploy the backend. - Updated
backend/.env.example,frontend/.env.vercel.example,docs/VERCEL_DEPLOYMENT.md,README.md,docs/README.md. Fixed a staleSUPABASE_SERVICE_ROLE_KEY→SUPABASE_SECRET_KEYname mismatch.
Env vars
Backend now needs: CLERK_SECRET_KEY (verifies Clerk tokens) and S3_ENDPOINT_URL / S3_REGION / S3_ACCESS_KEY_ID / S3_SECRET_ACCESS_KEY / S3_BUCKET_NAME (replacing the R2_* set). SUPABASE_URL / SUPABASE_SECRET_KEY stay (database).
How identity lines up
The frontend useAuth().user.id and the backend res.locals.userId are now the same Clerk user id, so existing ownership checks (row.user_id === user.id) work consistently across the stack.
Test plan
- Backend
tscbuild - passes. - Frontend
next build- passes. - Frontend tests (Clerk auth, branding) - pass.
- No
supabase.auth/auth.users/R2_references remain in code. - Manual end-to-end: requires a Supabase project + deployed backend (see
docs/SUPABASE_SETUP.md).
Notes
- Per-user data scoping: most tables filter by
user_id; Gary remains effectively single-user. No multi-tenant isolation was added (out of scope). - No backend test runner exists in the repo, so backend validation is via
tsc. Adding a test framework was left out of scope.
https://claude.ai/code/session_018GMAE164ehpBTxzBdsof9r
Generated by Claude Code
Our analysis
Complete backend Clerk migration and consolidate the data layer on Supabase — read the full analysis →
Think the analysis missed something the PR description covers?
Commits in this PR (1)
| SHA | Subject | Author | Date | |
|---|---|---|---|---|
a7ea67d0 | feat: verify Clerk tokens in backend, use Supabase for storage | Claude | 2026-05-20 | ↗ GitHub |
commit bodyComplete the Clerk migration on the backend side and consolidate the data layer onto Supabase so Gary can run without Cloudflare. - Backend auth: requireAuth verifies Clerk session JWTs via @clerk/backend instead of Supabase tokens; Clerk user id flows to res.locals.userId - Resolve user emails through the Clerk API (cached); the sharing/"people" endpoints now look users up via Clerk, not Supabase Auth - Schema: user_profiles and user_api_keys store the Clerk user id as text and no longer depend on auth.users; drop the Supabase Auth signup trigger - Account deletion removes the Clerk user plus the profile/api-key rows - Storage: switch the S3 client config from R2-specific env vars to generic S3_* vars (with a configurable region) so Supabase Storage works - Remove the dead frontend storage helper and the unused Supabase JWT helper - Docs: add docs/SUPABASE_SETUP.md and update env examples / deployment docs https://claude.ai/code/session_018GMAE164ehpBTxzBdsof9r | ||||
Capture this PR into my fork
Download a Markdown prompt that tells Claude how to port every
commit in this PR into your working tree. Run it via
claude -p < capture-pull-7.md from
inside the repo you want the changes in.