On October 30, 2026, Supabase stops exposing new tables to its Data API by default in existing projects. If your app was built with Lovable, Bolt or Cursor, you'll likely meet this the first time you add a table, and the quickest fix your AI tool offers can make that table readable by anyone.

What changes

  • From October 30, new tables you create in the public schema of an existing project can't be reached through the Data API (supabase-js, /rest/v1, GraphQL) until you grant access. Most new projects have worked this way since late May.
  • Tables you already have keep their grants and keep working.
  • Apps that connect to Postgres directly are not affected.

Source: Supabase changelog

What you'll see

The first query against a new table fails with:

code: "42501" message: "permission denied for table your_table" hint: "Grant the required privileges to the current role with: GRANT SELECT ON public.your_table TO anon;" 

The trap

Paste that error into an AI coding tool and it will usually do what the hint says, or go further and grant everything on every table.

A grant decides whether a role can reach a table. Row-level security (RLS) decides which rows it gets. If the table has no RLS, a grant to anon makes every row readable by anyone who opens your site, because the anon key ships in your JavaScript. Grant insert, update or delete as well, and the rows become writable too.

The safe fix

Do all three steps in the same migration. Replace your_table and user_id with your table and its owner column.

-- 1. Grant only what the app needs grant select, insert, update, delete on public.your_table to authenticated; -- Server code (Edge Functions) that uses the service role needs it too: grant select, insert, update, delete on public.your_table to service_role; -- Grant select to anon only if logged-out visitors must read this table. -- 2. Turn on row-level security alter table public.your_table enable row level security; -- 3. Let each user reach only their own rows create policy "owners manage their rows" on public.your_table for all to authenticated using (auth.uid() = user_id) with check (auth.uid() = user_id); 

If you hand this to your AI tool, ask it to keep the three steps together and never to use using (true) on tables that hold user data.

Check the tables you already have

It takes two minutes. Run this in the Supabase SQL Editor:

select c.relname as table_name, c.relrowsecurity as rls_enabled, has_table_privilege('anon', c.oid, 'select') as anon_can_read, has_table_privilege('authenticated', c.oid, 'select') as signed_in_can_read from pg_class c where c.relnamespace = 'public'::regnamespace and c.relkind in ('r', 'p') order by rls_enabled, table_name; 
  • rls_enabled false and anon_can_read true: anyone with your site's public key can read every row.
  • rls_enabled false and signed_in_can_read true: anyone who signs up can read every row.
  • rls_enabled true: the policies decide. Make sure none of them say using (true) on user data.

Missing row-level security is one of the most common holes in apps built with AI tools. I review those apps before launch, and the full guide, with copy buttons for the SQL, is at liftoffreview.com/supabase-oct-30. Happy to answer questions in the comments.