Back to journal

Web · 10 min · Jan 18, 2026

Supabase RLS patterns we actually use

JS
Jeeva Sasikumar

Row Level Security is the difference between a demo and a multi-tenant product. These are the patterns we reuse on almost every Supabase project.

Tenant column + membership join

Every tenant-scoped table gets an organization_id. Policies check auth.uid() against a memberships table rather than embedding roles on the user row. That keeps invites, roles, and soft-deletes simple.

Separate read and write policies

One policy for SELECT, another for INSERT/UPDATE/DELETE. Readers and editors rarely share the same constraints. Collapsing them into a single USING/WITH CHECK block is how subtle privilege bugs sneak in.

Service role stays server-only

  • Never expose the service role key to the browser.
  • Use it only in trusted server functions for admin jobs and webhooks.
  • Prefer user-scoped clients for product paths so RLS is always exercised.

Test policies like application code

Spin up two users in different orgs and assert denied reads. Policy regressions are silent until a customer emails you a screenshot of someone else's data.