Owner-only table
Each signed-in user sees and changes only their own rows.
-- public.your_table: owner only
alter table public.your_table enable row level security;
revoke all on table public.your_table from anon, authenticated;
grant select, insert, update, delete on table public.your_table to authenticated;
grant select, insert, update, delete on table public.your_table to service_role;
drop policy if exists your_table_select_own on public.your_table;
create policy your_table_select_own on public.your_table
for select to authenticated
using ( (select auth.uid()) = user_id );
drop policy if exists your_table_insert_own on public.your_table;
create policy your_table_insert_own on public.your_table
for insert to authenticated
with check ( (select auth.uid()) = user_id );
drop policy if exists your_table_update_own on public.your_table;
create policy your_table_update_own on public.your_table
for update to authenticated
using ( (select auth.uid()) = user_id )
with check ( (select auth.uid()) = user_id );
drop policy if exists your_table_delete_own on public.your_table;
create policy your_table_delete_own on public.your_table
for delete to authenticated
using ( (select auth.uid()) = user_id );
What you need first
A column that stores the user's id as a uuid, here user_id. auth.uid() returns the id of the user making the request, and returns null when nobody is signed in.
Why four policies
| Operation | Clause | Effect |
|---|---|---|
| select | using | Only rows whose owner is the caller are visible. |
| insert | with check | A user cannot create a row that belongs to someone else. |
| update | using + with check | Only own rows can be changed, and the owner cannot be reassigned. |
| delete | using | Only own rows can be deleted. |
to authenticated stops the policy from running for signed-out requests. Wrapping the call as (select auth.uid()) lets Postgres evaluate it once per statement instead of once per row; both forms come from Supabase's RLS guide.
We ran this SQL against Postgres on 2026-11-10: a second user read zero rows, and an insert with another user's id was rejected.
Write the fix for your tables
One table is free. The whole schema with a rollback file is $9 one-time, free during launch.
Waiting for a paste.
Who can reach each table
Your tables show up here, one row each, with a choice of who can reach them.
Parsed in your browser. Your SQL is never uploaded, and no site or database is scanned.
Questions
My owner column is text, not uuid.
The comparison with auth.uid() will fail. The tool warns about this and does not add a cast for you.
Sources
- Supabase docs: Row Level Security, read 2026-11-10
- Supabase docs: Securing your API, read 2026-11-10
- Our own test: generated SQL run against PGlite (Postgres compiled to WebAssembly), read 2026-11-10
Related
- Supabase table not exposed to the Data APIA new table is invisible to the API until a role is granted access. Here is the grant.
- Is my Supabase project affected?Run one query. No rows means new tables get no grants.
- "Grant the required privileges to the current role with: GRANT ..."The hint names one missing grant. Paste it and get the full set.
- Private, server-only tableNo access from the browser. Your server reads and writes.