database · tutorial · comparison

Supabase Starter That Keeps Your Exit Open

Supabase Auth plus Postgres plus storage — wired with portable seams so the bundle never becomes a trap. The honest starter inside.

Residents on the iron fire escapes of a New York tenement, early 1900s
Residents on the iron fire escapes of a New York tenement, early 1900s · Photo: Bain News Service, public domain, via Wikimedia Commons

My Supabase honeymoon ended at the invoice

Month one with Supabase is bliss.

Auth in an afternoon.

Postgres without provisioning.

Storage with a dashboard.

Month four brings the egress line. Month six, the row-count tier math. Month nine, the quiet realization that Auth users, storage paths and row policies have woven the app into one vendor's shape.

Nothing is overpriced in isolation. Everything grows load-bearing in combination.

Leaving means an auth migration, URL rewrites and policy translation. A quarter nobody budgets, because "free tier" made deciding feel unnecessary.

Enjoy the bundle fully. Plan the exit first.

Bundled or seamed

Layer Welded, the typical path Seamed
Auth users Supabase-only sessions Your own user rows
Storage URLs Supabase paths everywhere Your own domain, portable
Access logic Row policies as the only guard App guards first, policies optional
Queries Supabase-JS idioms inline Shared actions, portable
Exit effort Large Bounded by design

The bundle teaches three lessons.

Row policies become architecture by accident. They start as security and end as business logic. Tier checks, ownership rules, visibility filters. Logic that exists nowhere else.

App guards first. Policies second.

Storage paths are forever addresses. Every vendor URL baked into rows, emails and caches is a migration line item. Your own domain costs the same effort today and moves free tomorrow.

Auth depth is the deepest hook. Sessions keyed to your own user rows migrate as data. Vendor-shaped sessions migrate as projects. Own identity, rent features.

What to do

Own identity. Rent the database.

Sign-in runs through the kit's own Google and email-link flows. A Supabase adapter stores users in your Supabase tables, but the logic about who is who stays in the app.

The vendor holds rows. Your app holds the meaning.

OAuth identities re-link by email after any future move. Magic links work against migrated rows immediately.

Store files at addresses you control.

Uploads go from the browser straight to object storage like R2 or S3, and are served from your own domain. The database stores your addresses, never a vendor's paths.

Storage then migrates with a DNS change and a copy.

Guard in app code first. Add policies as armor.

The app checks ownership and paid access in code, which is reviewed, tested and portable. Supabase's row-level policies can sit on top as extra protection, but the kit does not depend on them.

When engines change, app guards move as they are.

Bundled or seamed, over time

Event Native path Portable alternative
Download traffic grows Pay and wince Files already on your own storage
Tier pressure Upgrade or migrate Switch engines
An auth export A project Rows move as data
Policy translation A rewrite from scratch App guards carry over

Seams cost upfront thought. URL design, app-level guards, identity mapping. Roughly an afternoon per layer, before any pain exists.

BoringPush does not publish exit timings. Rehearse your own.

Know this first

BoringPush does not publish exit timings. It ships no realtime layer.

Rehearse your own exit before you need it.

Why BoringPush is the way to do this

Enjoy Supabase. Don't depend on it.

BoringPush stores users in your tables, serves files from your own addresses and guards access in app code.

The bundle is optional. Moving stays possible.

FAQ

Is Supabase still worth it versus SQLite first?

For Supabase-committed teams, absolutely: managed Postgres, a dashboard and quick setup are real advantages. For engine-agnostic solos, start with SQLite and switch later if needed. The seams serve both: committed users get safety rails and future-switchers keep the door open.

How do realtime subscriptions fit?

Treat Supabase realtime as the current transport and keep the meaning of a subscription in app code. BoringPush does not ship a realtime layer, so if you add one, keep a polling fallback for engines without push.

Should row-level policies be strict if app guards already check?

Defense in depth earns its keep exactly once, on the day a bug bypasses app code. Strict policies plus primary app guards are belt and suspenders, each cheap. Document the intent so policies can be rewritten for another engine.

What is hardest to migrate if I leave?

Habit, not data. Dashboard workflows such as the table editor and log tailing have no equivalent at the same polish. Rows, files and guards move quickly; fingers take a month.

Can I run hybrid, with managed auth and local data?

It is a legitimate steady state when chosen on purpose. BoringPush's own auth plus a local SQLite file, or Supabase for data only, are both supported. Document why you chose it and revisit it yearly.

When should the exit be tested, not just built?

Once a year, on staging only: export users, copy a storage bucket and translate one policy, timed. Untested exits are intentions, and drilled exits are options.

Keep reading

+ all the boring stuff (analytics, auth, SQLite ↔ Supabase, full marketplace, webhooks)

The Agent-First Shipping Kit

Command from your phone.

Built so autonomous AI agents build what you want without breaking what's already paying you.

$199

Get BoringPush

One-time payment · Lifetime updates · Unlimited projects.

Keep reading