Fix Supabase "new row violates row-level security policy" on client insert
The problem
Inserting from the client threw:
PostgrestException: new row violates row-level security policy for table "documents" (code 42501)
The same insert worked fine in the SQL editor (which uses the service role) — because RLS was enabled and no policy matched the authenticated role for INSERT.
What didn't work
alter table documents enable row level security;and expecting a default policy — there is NO default; with RLS on and no policy, every client operation is denied.- A
for allpolicy withoutto authenticated— it defaults topublic, which behaves differently for the service role and anon role and silently misses the actual caller. - Disabling RLS from the dashboard — everything works and the table is now readable/writable by any anonymous request.
The fix
alter table documents enable row level security;
create policy "users can insert their own documents"
on documents for insert
to authenticated -- role matters: anon is a different role
with check (auth.uid() = user_id); -- WITH CHECK validates the NEW row
create policy "users can read their own documents"
on documents for select
to authenticated
using (auth.uid() = user_id); -- USING filters EXISTING rows
// trusted server-side writes that must bypass RLS use the service role key — server ONLY:
import { createClient } from '@supabase/supabase-js';
const admin = createClient(process.env.SUPABASE_URL!, process.env.SUPABASE_SERVICE_ROLE_KEY!);
// admin.from('documents').insert(...) — RLS not applied
Why it works
RLS has no implicit allow — each role needs a matching policy per command, WITH CHECK governs rows being written while USING governs rows being read, and the service-role key is the sanctioned RLS bypass for server code that must never ship to the browser.