BoilerplateHub

Next.js + Turso + Better Auth + Stripe

Next.js with Turso, Better Auth and Stripe is built for per-tenant isolation. A control database holds users, tenants and subscriptions while each customer gets their own SQLite database. It suits a business tool whose buyers ask where their data physically lives and expect a specific answer.

Next.js TursoBetter AuthStripe

Database

Turso

Turso pushes you toward one database per tenant, which changes where everything lives. Keep a control-plane database holding Better Auth's users, the tenant registry and the Stripe subscription rows, then give each customer a separate SQLite database for their working data. Billing questions get answered without opening a tenant database at all, and a departing customer becomes a database you drop rather than a delete cascade you hope covered every table.

Authentication

Better Auth

Better Auth fits because its schema is ordinary SQL running against SQLite through a query-builder adapter, so sessions live in the control-plane database alongside the tenant mapping. A request resolves the session, looks up which tenant database to open, then connects. That indirection is the entire point of the architecture, and it belongs in one shared helper rather than spread across route handlers where somebody will eventually skip it.

Payments

Stripe

Stripe bills the tenant rather than the individual, which lines up exactly with the database boundary you already drew. Put the Stripe customer ID on the tenant row and derive seat counts by counting users mapped to it. Staying merchant of record means you can invoice, accept purchase orders through the API and quote annual terms, all of which come up far more often in a per-tenant product than in a consumer one.

What to watch out for

SQLite permits one writer per database, so the control plane is a shared bottleneck that every request touches for session and tenant lookup while Stripe events write to it too. Keep it small and read-dominated. The other standing cost is migrations: a schema change to tenant data must be applied to every tenant database individually, and that fan-out job is something you build and operate yourself.

Boilerplates close to this stack

Matched on Next.js 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 Next.js SaaS on Turso, Better Auth and Stripe.

## Stack

- Framework: Next.js
- Database: Turso
- 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