The Ideal Stack for a Modern SaaS
Next.js, Supabase, and Stripe form the most powerful technology trio for building a SaaS in 2025. Next.js handles rendering and routing, Supabase provides the PostgreSQL database with authentication and RLS security, and Stripe takes care of all payment logic. Together, they cover 90% of a SaaS technical requirements.
This guide walks you through the architecture of a complete SaaS step by step. If you prefer to start from a ready-made foundation, Boilerplate-Stack already integrates this entire architecture.
Step 1: Account-Centric Architecture
The most common mistake in SaaS is attaching data directly to users. Instead, adopt an account-centric model:
USER (auth.users)
│
│ can have multiple
▼
MEMBERSHIPS (user_id, account_id, role)
│
│ belongs to
▼
ACCOUNT (personal | workspace)
├── Credits Balance
├── Subscription (Stripe)
├── AI Sessions
└── API KeysThis model enables:
- A user can belong to multiple organizations
- Roles are specific to each organization
- Billing is tied to the account, not the user
- Transitioning from B2C to B2B is seamless
Step 2: Authentication with Supabase Auth
Supabase Auth offers several authentication methods. For a SaaS, magic links are recommended:
- Enhanced security: no passwords to store or compromise
- Smooth experience: one click in the email is enough
- OAuth compatible: add Google, GitHub alongside
Critical point: on the server side, always use getUser() and never getSession(). The session can be forged, while getUser() validates the token with Supabase.
Row Level Security (RLS)
Every table must have an RLS policy that filters by account membership:
CREATE POLICY "members_access" ON table_name FOR SELECT
USING (EXISTS (
SELECT 1 FROM memberships
WHERE memberships.account_id = table_name.account_id
AND memberships.user_id = auth.uid()
));RLS is your last line of defense. Even if a bug exposes an API route, the data remains protected at the database level.
Step 3: Billing with Stripe Webhooks
Stripe billing must be webhook-driven, not based on synchronous API calls. Here is the flow:
- checkout.session.completed → Subscription creation or credit purchase
- customer.subscription.updated → Plan change synchronization
- customer.subscription.deleted → Mark as canceled
- invoice.paid → Monthly credit refill
Plans are defined in code (configuration file), not in the database. The webhook matches the received stripePriceId to the configured plan.
Atomic Credit System
Credit operations must be atomic via SQL RPC functions:
-- Never UPDATE credits_balance directly
SELECT decrement_credits(p_account_id, p_amount, p_reason);
SELECT add_credits(p_account_id, p_amount, p_source);Step 4: OWASP Security
A production SaaS must cover the OWASP Top 10:
- CSRF: Double Submit Cookie pattern
- Rate limiting: Upstash Redis with tiers (strict, standard, relaxed)
- Sanitization: clean all user inputs
- CSP headers: strict Content Security Policy
- Bot protection: Cloudflare Turnstile on sensitive forms
Step 5: Deployment
With Next.js on Vercel and Supabase in the cloud, deployment is straightforward. For self-hosting, an optimized multi-stage Dockerfile is essential.
The Turnkey Solution
Implementing all of this from scratch easily takes 3 to 5 months. Boilerplate-Stack gives you this complete architecture, tested and documented, ready to customize for your project.
Every pattern described in this article is implemented in Boilerplate-Stack. The code is structured to be readable, extensible, and compatible with AI-assisted development.
Explore the full documentation and demos at boilerplate-stack.com.