Skip to content

Supabase RLS: the policy patterns I actually use

Kevin TrinhFebruary 28, 2026

Five Row-Level Security policies that cover 90% of real apps — owner-only, team-scoped, soft-delete, public read with private write, and admin override. Copy-paste ready.

Supabase dashboard

Row-Level Security is one of those Postgres features that feels intimidating until you have a few patterns memorized. After shipping ~6 apps on Supabase, here are the ones I copy-paste constantly.

1. Owner-only

The classic: a user can only see or modify their own rows.

CREATE POLICY "Owner read" ON tasks
FOR SELECT USING (auth.uid() = user_id);
 
CREATE POLICY "Owner write" ON tasks
FOR ALL USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);

The WITH CHECK is critical for inserts/updates — without it, a user could insert a row with someone else's user_id.

2. Team-scoped

Users see anything in a team they're a member of.

CREATE POLICY "Team read" ON projects
FOR SELECT USING (
  team_id IN (
    SELECT team_id FROM team_members WHERE user_id = auth.uid()
  )
);

3. Soft-delete

Hide rows where deleted_at is set, except to admins.

CREATE POLICY "Hide soft-deleted" ON posts
FOR SELECT USING (
  deleted_at IS NULL
  OR auth.jwt() ->> 'role' = 'admin'
);

4. Public read, owner write

For things like blog posts or project showcases.

CREATE POLICY "Anyone can read" ON posts
FOR SELECT USING (true);
 
CREATE POLICY "Only owner can write" ON posts
FOR INSERT WITH CHECK (auth.uid() = author_id);
 
CREATE POLICY "Only owner can update" ON posts
FOR UPDATE USING (auth.uid() = author_id);

5. Admin override

A "skip RLS" door for service code, without disabling RLS globally.

CREATE POLICY "Admin override" ON anything
FOR ALL USING (auth.jwt() ->> 'role' = 'admin');

Then call this from your server with the service role key — your client code stays locked down.

The one rule I follow

Never disable RLS on a production table. If a query needs to bypass it, that query should be running with the service role key from a server, not the anon key from a client. That single rule has saved me from a whole class of bugs.