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.