12 Jul 2026 · 2 min read
Supabase Row-Level Security: the 15-minute mental model
RLS moves "who can see what" out of your API and into the database. Here's the minimum you need to use it safely.
In a typical backend, you write checks like if (order.userId !== session.userId) throw. Forget one and you have a data leak. Row-Level Security (RLS) flips this: the database itself filters rows per user, so a missing check in your code cannot expose data.
Step 1: turn it on
RLS is off by default. A table without it is readable by anyone holding the public key.
alter table orders enable row level security;Once enabled with no policies, nobody can read or write the table. That is the safe default. Now you add rules.
Step 2: write policies
A policy is a predicate evaluated per row. auth.uid() returns the signed-in user's id.
create policy "customers read own orders"
on orders for select
using (auth.uid() = user_id);
create policy "customers create own orders"
on orders for insert
with check (auth.uid() = user_id);using filters rows you can see; with check validates rows you try to write. Forgetting with check on inserts is the most common mistake.
Step 3: know what bypasses it
- The service role key skips RLS entirely. Keep it on the server, never in the browser
- Views run with their owner's rights unless configured otherwise
- Functions marked
security defineralso bypass it, so audit them
Step 4: test as the user, not as admin
Your SQL editor usually runs as a privileged role, so everything looks fine. Test through the client with a real user session, and write at least one test that proves user A cannot read user B's row.
A practical layout
- Public catalogue tables:
selectallowed for everyone, writes only for staff - Personal data (orders, wishlists): policies keyed on
auth.uid() - Admin features: a
roleclaim or a separatestafftable checked inside the policy
What you gain
You can ship a storefront where the browser talks to the database directly and still sleep well, because the rule that matters is enforced in the one place no client can reach around.