Dhaka--:--
All notes

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.

  • Supabase
  • PostgreSQL
  • Security

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 definer also 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: select allowed for everyone, writes only for staff
  • Personal data (orders, wishlists): policies keyed on auth.uid()
  • Admin features: a role claim or a separate staff table 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.