Boilerplate-Stack
Back to blog
Articles

How to Build a SaaS with Next.js, Supabase, and Stripe

|
3 min read

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 Keys

This 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:

  1. checkout.session.completed → Subscription creation or credit purchase
  2. customer.subscription.updated → Plan change synchronization
  3. customer.subscription.deleted → Mark as canceled
  4. 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.