Building a SaaS MVP with Supabase: Auth Was Fast. The Decisions Around It Weren't.
Building a SaaS MVP with Supabase: Auth Was Fast. The Decisions Around It Weren't.
Supabase auth implementation is fast. That's not the hard part.
The hard part is what comes after auth works — how far to design RLS, whether to add Edge Functions, whether to layer in custom JWT claims. That's where the time goes. Not tool learning. Design decision cost.
Conclusion up front: basic Supabase Auth took under 2 hours. RLS design took a full day. If I'd known that asymmetry going in, I would have ordered the work differently.
Where the Time Actually Went
I built email/password auth and Google OAuth using Supabase + Next.js.
| Phase | Time | |---|---| | Supabase Auth setup (email + Google OAuth) | 1.5h | | Session management + client-side route guards | 2h | | RLS design (which tables, what policies) | ~8h | | Basic CRUD (3 tables) | 3h | | Edge Functions decision + initial implementation | 4h |
RLS took longer than auth itself.
The reason is simple. RLS is one SQL line. But knowing what to protect has to happen before that line gets written. If the table design is wrong, changing it later means rewriting all the policies. That's not a tool problem — it's a sequencing problem.
Decision 1: Use Supabase Auth As-Is, or Layer In Custom JWT?
This is the first thing that slows you down.
Supabase Auth issues JWTs, but the default claims are minimal. Once you start thinking about multi-tenant SaaS or complex role-based access, you hit a fork: add custom claims or not.
I chose to use Supabase Auth as-is. Three reasons:
- Adding custom JWT complexity in the MVP phase isn't worth the flexibility it buys
- Role management can live in a separate table and be controlled via RLS
- Adding "maybe we'll need this" architecture early is speculation dressed as engineering
There are cases where custom JWT is the right call — integrating external identity providers, or needing a shared JWT across systems outside Supabase. Neither applied here.
Building Ordia taught me something: don't add complexity based on predictions. Build simple, change when necessary — except where changing later is expensive. Auth scheme changes are expensive. That's why I made this call early.
Decision 2: Which Tables Get RLS, and How Far Does It Go?
The recommendation is to enable RLS on all tables by default. That's not the hard part. Writing the policies is.
My mistake was starting to write RLS before the table design was stable. Every schema change meant rewriting policies. That's where the 8 hours went.
The correct sequence:
- Finalize the data model (who reads and writes whose data)
- Translate those access boundaries into RLS policies
- When the schema changes, update the policies at the same time
After establishing this order, RLS design for a new table takes about 30 minutes.
One specific thing worth noting for multi-tenant setups: policies that only rely on auth.uid() aren't sufficient for tenant isolation. Design in organization_id filtering from the start — at the MVP stage. Adding it later means rewriting every existing policy. I did this the hard way and lost time on it.
Decision 3: Stay Serverless, or Add Edge Functions?
The ideal Supabase setup is serverless. Clients talk directly to Supabase. RLS handles authorization. It's simple and fast.
But it breaks the moment logic appears that can't be exposed to the client.
I added Edge Functions when Stripe webhook processing became necessary. Stripe secrets can't go client-side. That was the end of the fully serverless approach.
What I learned from Edge Functions:
- Cold starts exist. Not suitable for latency-sensitive endpoints.
- Deno runtime. Not all Node.js libraries work out of the box.
- Async, low-frequency processing — webhook handling, third-party API calls — they're fine for this.
The strategy of starting serverless and adding Edge Functions only where necessary was correct. There was no reason to stand up Express from day one. Ordia follows the same logic: no backend codebase to maintain means lower long-term cost.
Technical Debt I Left in the MVP
Honest account. These three were expensive to fix later.
Multi-tenant schema design
Started with single-user table structure, converted to multi-tenant later. Rewrote all RLS policies. organization_id should go into every table from the start.
RLS policy testing strategy RLS is testable via SQL Editor, but I had no systematic approach to verify policies were behaving as intended. Hit an RLS-caused bug in production.
Edge Functions local dev environment Because I added them late, local development became awkward. Should have set up Supabase CLI local environment from the beginning.
Supabase auth implementation genuinely is fast. The implementation cost is close to zero. But capturing that speed requires finalizing the data model and RLS design first. Skip that step and you're not saving time — you're borrowing it.
For solo technical founders building a SaaS MVP, Supabase is still the most rational choice I've found. But "fast with Supabase" is more precisely "fast if the design decisions are made upfront." Speed is a function of judgment quality, not tool performance.
