BoilerplateHub

Nuxt + Supabase + Better Auth + Stripe

Nuxt with Supabase, Better Auth and Stripe puts every database and billing call inside Nitro server routes. Supabase supplies the Postgres, Better Auth supplies a session your server middleware resolves, and Stripe supplies billing you write yourself. It suits a Vue team building a subscription product with pricing logic of its own.

Nuxt SupabaseBetter AuthStripe

Database

Supabase

Supabase gives Nuxt a Postgres that Nitro routes query with an ordinary driver, which is the right shape once Better Auth owns the user tables and the client library's row level security path is off the table. Keep every database call inside server/, surface results through useFetch, and put the Stripe mirror in the same schema so one statement returns both the profile and the plan a page needs to render.

Authentication

Better Auth

Better Auth ships a Nuxt integration, and the part worth getting right is that session reads belong in server routes and server middleware rather than in a composable that will run on both sides of hydration. Its tables sit in the Supabase Postgres under your migrations, so the Stripe customer ID is a column and not a foreign system. Add the organization plugin if Stripe will bill teams, since middleware can then resolve company and entitlement together.

Payments

Stripe

Stripe from Nitro is unremarkable in the good sense: one defineEventHandler creates a checkout session, another verifies the webhook, and the Node SDK works because Nitro is Node. You keep merchant of record status and everything it implies, tax registration included once you sell across borders, in exchange for billing logic that lives in your repository and changes with a deploy rather than a dashboard visit.

What to watch out for

Auto-imports and universal rendering make it easy to write a composable that touches the Better Auth server API from code which also runs in the browser, leaking a secret or failing at hydration depending on the day. Keep the auth client and the auth server strictly apart. The Stripe secret key belongs in the private section of runtimeConfig, never under public, where one mistaken read ships it to every visitor.

Boilerplates close to this stack

Matched on Nuxt plus the parts of this stack our catalog tags. Each card shows which pieces actually line up, so you can see how much you would still wire yourself.

A CLAUDE.md for this stack

The rules that matter for this combination specifically, including who owns entitlement state. Adapt the commands to your repository before committing it.

CLAUDE.md
# CLAUDE.md

This project is a Nuxt SaaS on Supabase, Better Auth and Stripe.

## Stack

- Framework: Nuxt
- Database: Supabase
- Auth: Better Auth
- Payments: Stripe

## Boundaries

- Never edit a migration that has already run. Write a new one.
- Never hardcode Stripe price or product identifiers in components. They belong in config.
- Never trust a client-supplied user id. Read the session from Better Auth on the server.
- Treat webhook handlers as idempotent. The same event will arrive twice.

## Entitlements

- Stripe is the source of truth for what a customer paid for.
- The database mirrors that state; it never decides it.
- Any check for "can this user do X" reads the mirrored entitlement, not a live API call.

## Before you say a change is done

- The app builds.
- Tests pass.
- No secret, key or webhook signing secret appears in a committed file.

Related stacks