BoilerplateHub

Expo + Supabase + Better Auth + RevenueCat

Expo with Supabase, Better Auth and RevenueCat is the subscription configuration the app stores actually permit, with identity kept in your own database rather than a vendor's. RevenueCat normalises store purchases into entitlements, Supabase holds the content those entitlements unlock, and Better Auth ties one person to both.

Expo SupabaseBetter AuthRevenueCat

Database

Supabase

Postgres here stores content and a copy of RevenueCat's entitlement verdict, never a subscription model of your own design. Because Better Auth replaces the bundled auth, that copy is read by your API rather than by row level security policies, so premium queries are filtered in server code against the cached entitlement. Keep the RevenueCat app user ID on the Better Auth user row and treat every write to the cache as webhook-only.

Authentication

Better Auth

Better Auth's Expo support gives you a stable user ID held in secure device storage, and that ID is what you pass to RevenueCat at login instead of letting it generate an anonymous one. Do that and a subscriber who reinstalls, or moves between platforms, signs in to find the purchase already attached. Handle the alias case where somebody subscribes before creating an account, since those two identities must be merged rather than left running in parallel.

Payments

RevenueCat

RevenueCat is present because Apple and Google require their own purchase systems for digital subscriptions consumed inside the app, and writing receipt validation and renewal handling for both platforms is a project in itself. Entitlements give the client an instant local answer and give your server a webhook-fed one. Store commission applies underneath all of it, so the pricing has to survive that cut before anything else in this stack matters.

What to watch out for

Three identities are in play: the store's, RevenueCat's and Better Auth's, and only the last is under your control. Someone who buys on a family member's store account, or signs into your app with a different address than the one attached to their device, produces a mismatch that reads as a missing purchase and cannot be repaired from your database. Decide the support path before launch, because it usually ends at the store.

Boilerplates close to this stack

Matched on Expo 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 Expo SaaS on Supabase, Better Auth and RevenueCat.

## Stack

- Framework: Expo
- Database: Supabase
- Auth: Better Auth
- Payments: RevenueCat

## Boundaries

- Never edit a migration that has already run. Write a new one.
- Never hardcode RevenueCat 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

- RevenueCat 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