next.js · supabase · vercel

The piece every B2B app needs, already in place.

A B2B SaaS starter kit built around the one thing most templates skip: keeping one company's data completely invisible to another's — enforced by the database itself, not application code you have to remember to write correctly every time.

postgres acme_inc members: 4 projects: 3 ✓ query allowed globex_inc members: 9 projects: 12 ✕ blocked by policy acme session
Same login session, same query shape. Acme's own org resolves normally — Globex's rows are rejected by Postgres before they ever reach the app.

What's inside

Everything a team-based B2B product needs before you can build the part that's actually yours — already wired together and tested end to end.

Email + GitHub OAuth login
Password confirmation, show/hide toggle, protected routes via middleware.
Organizations & team membership
Users belong to a company, not just an account — the model almost every free template skips.
Row Level Security, scoped per org
Enforced at the database layer. One company's data cannot leak into another's, even if the app code has a bug.
Invite teammates, auto-join on signup
A database trigger checks pending invites the moment a new user signs up and joins them automatically.
Team member list with real email lookups
Done the secure way — the service role key stays server-side, never reaches the browser.
One example resource, fully wired
A "projects" table showing the complete create/read/delete + RLS pattern — rename it for whatever you're building.
Setup docs + one-shot SQL schema
Clone, run one script, paste in your Supabase keys, deploy.

The bug I had to fix first

RLS is worth the setup pain, but it has sharp edges. Here's the one that actually happened while building this.

01

A policy that queried itself

The membership policy checked "does this user belong to this org" by querying the membership table — the same table the policy was protecting. Every read triggered the policy again, which triggered another read.

02

Postgres said so, plainly

infinite recursion detected in policy for relation "organization_members" — an easy mistake to make, because the rule reads as completely correct in English.

03

A function that steps outside the check

A security definer function reads membership without re-triggering the policy asking the question — breaking the loop while keeping the rule intact.

-- runs with elevated privileges, bypassing RLS internally
create or replace function get_user_org_ids()
returns setof uuid
language sql
security definer
set search_path = public
as $$
  select organization_id from organization_members where user_id = auth.uid()
$$;

Stack

Nothing exotic — three tools most teams already trust, wired together correctly.

Next.js
App Router, server actions, middleware-based route protection
Supabase
Auth, Postgres, Row Level Security, database triggers
Vercel
Deploy target, environment config included

Get it

One payment, the full source, yours to build on.

Full source, zip download

Auth, organizations, RLS, invites, team list, example resource, setup docs, and a one-shot SQL schema file.

Being upfront about v1: invites don't send an email yet (you tell the person to sign up manually), and there's no billing integration yet. Both are planned.
$49
one-time
Get the kit