TLDR: Prove your numbers on a stateless preview route before you touch the database. In that order, every time.
The Setup
an ecommerce business runs paid webinars with KOL (Key Opinion Leader) doctors — real production events, with affiliates, Shopify orders, Klaviyo email flows, and a Zoom room full of people who actually paid to be there.
V1 of the dashboard handled Zoom + Shopify CSVs. Enough to start.
But what the team actually needed was a lot more: affiliate attribution by Everflow partner, email/SMS flow analytics, post-event video engagement, the full funnel from reg-page clicks to revenue. That's V2.
Phase 4: Prove the Numbers. No Database.
Before I touched Supabase, I built a /preview route.
No DB. No persistence layer. Just: drop in the CSV files, run the metrics engine in-memory, render the dashboard in the browser.
I verified it live via Arc CDP (my browser automation shortcut for screenshotting and testing local pages). Affiliate rows: 15 — with live Everflow partner names resolving automatically. A health media company, a wellness influencer, a gut-health brand… every one of them.
Every number was correct.
That's the moment you know you're ready for Phase 5.
The Wall I Hit With Supabase
an ecommerce business already had a production Supabase project running 84 tables. Not touching that namespace.
So: isolated Postgres schema. webinar_v2. Same project, zero collision risk.
Here's where I wasted time.
I passed ?schema=webinar_v2 in the connection URL — the standard Prisma approach. Works great with the classic Prisma client.
Did absolutely nothing here.
Because @prisma/adapter-pg (Prisma's newer driver adapter) ignores ?schema= in the URL entirely. Queries hit the public schema. Nothing persisted where it was supposed to.
The fix was this:
new PrismaPg(cfg, { schema: 'webinar_v2' })
One constructor option. Costs you nothing. Took me longer than I'd like to admit to find it. It's in the docs. I missed it.
The GA4 Sidequest
While I was debugging that schema issue, I was also fighting Google Analytics 4's API to auto-pull reg-page click data.
Service account auth? Blocked. (The property setup wouldn't allow it.) Workaround: OAuth-as-user — the authenticated user's token grants API access directly. Less elegant than a headless service account. Fully functional. Sometimes you ship the workaround and document it.
Verified End-to-End
npx tsx scripts/persist-reference.ts — reference event written and read back from webinar_v2:
- 92 orders
- 15 affiliate rows with live Everflow partner names
- 20 email/SMS flow messages
- 136 post-video engagement points
- 826 timeline points
DONE.
Why This Matters to Me
I used to reach for the database in Phase 1. When numbers came out wrong, I'd spend hours chasing ghosts — was it the CSV parser? The DB write? The query? The adapter?
The no-DB /preview pattern cuts all of that.
Your metrics engine stands alone. You KNOW the numbers are right before persistence even enters the picture. When Phase 5 breaks, you know exactly where to look.
That's not a trick. That's just good architecture.