Guides

Supabase October 30, 2026 change: new tables are no longer exposed to the Data API automatically

Supabase is changing a default that many AI-built apps rely on. From October 30, 2026, new tables in the public schema of existing projects are no longer exposed to the Data API automatically. Old tables keep working. New tables return a permission error until you add GRANTs. Here is what that means and how to fix it safely.

Source: Supabase changelog, Breaking Change: Tables not exposed to Data and GraphQL API automatically. This guide summarizes it. Check the changelog for the latest details.

1. What is changing

Until now, a Supabase project with default settings granted select, insert, update and delete on every table in the public schema to the anon, authenticated and service_role roles. So every new table was reachable through the Data API as soon as you created it.

Once the change reaches your project, a new table in public needs an explicit Postgres grant before the Data API can see it. The Data API is Supabase's auto-generated REST and GraphQL layer, which supabase-js and the other client libraries call.

2. The dates

  • April 28, 2026: new projects could opt in to the new behavior at creation.
  • May 30, 2026: the new behavior started to become the default for new projects, rolled out gradually.
  • October 30, 2026: the new behavior is applied to all existing projects.

3. What breaks, and what does not

  • Existing tables keep working. They keep their current grants and stay reachable.
  • New tables are not reachable through the Data API until you grant access. A request returns an error like this:
{
  "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;"
}

This matters most for AI-built apps. If your AI tool, a CLI script or a migration creates a new table and your app expects to read it with supabase-js straight away, that feature will fail until the grants are added.

4. Who needs to act

Supabase says you need to act if any of these are true:

  • Your app reads or writes tables in public through the Data API (PostgREST, GraphQL, supabase-js or another client library, or direct HTTP to /rest/v1/ or /graphql/v1).
  • Your migrations or setup flow create tables in public without explicit GRANT statements.
  • Your AI coding tool, a CLI script or a Management API call creates tables and expects them to be reachable through the Data API right away.

If you only use a direct Postgres connection (psql, an ORM, or an app server with a connection string), Supabase says you are not affected.

5. The safe fix: grant, enable RLS, add policies

Supabase recommends treating three steps as one unit for every new table you want to expose. If the grant is missing, Postgres rejects the query before RLS comes into play. If the grant is there but RLS is off, the role can reach every row. You need all three.

Start by deciding what each role really needs:

  • anon is anyone who is not logged in, including anyone who copies your public key. Grant it nothing unless logged-out visitors truly need the table, and then usually only select.
  • authenticated is any logged-in user. Grant only the actions your app uses, and let RLS policies limit which rows each user can touch.
  • service_role is for server code only. Its key skips RLS, so it must never be in browser code.

For a private table, like a user's notes, that often means no access for anon at all:

-- 1. Grant only what each role needs (nothing for anon here)
grant select, insert, update, delete on public.notes to authenticated;
grant select, insert, update, delete on public.notes to service_role;

-- 2. Turn on Row Level Security
alter table public.notes enable row level security;

-- 3. Add real policies, scoped to the signed-in user
create policy "Read own notes" on public.notes
  for select to authenticated
  using ((select auth.uid()) = user_id);
create policy "Insert own notes" on public.notes
  for insert to authenticated
  with check ((select auth.uid()) = user_id);
create policy "Update own notes" on public.notes
  for update to authenticated
  using ((select auth.uid()) = user_id)
  with check ((select auth.uid()) = user_id);
create policy "Delete own notes" on public.notes
  for delete to authenticated
  using ((select auth.uid()) = user_id);

For data that really is public, like published blog posts, you can grant select to anon and limit it with a policy. Do not give anon write access:

grant select on public.posts to anon, authenticated;
grant select, insert, update, delete on public.posts to service_role;
alter table public.posts enable row level security;
create policy "Anyone can read published posts" on public.posts
  for select to anon, authenticated
  using (published = true);

Supabase's own example in the changelog grants select to anon and full access to authenticated and service_role, then enables RLS and adds a policy. Adjust the grants to your table. A narrower grant is safer.

What to avoid

  • Do not run a blanket grant to make the error go away, such as grant all on all tables in schema public to anon. That can expose tables you meant to keep private.
  • Do not leave RLS off on a table you grant to anon or authenticated.
  • Do not use using (true) or with check (true) on insert, update or delete policies. They let any visitor write.

6. Make it part of every new table

Supabase suggests putting the GRANT statements in the same migration as enable row level security and the policies. If an AI tool creates your tables, update its instructions (or use the Supabase agent skill, which Supabase says includes the grants step) so each new table gets all three steps.

Paste this into Cursor, Lovable, Bolt or another AI tool to review your project:

Fix prompt · paste into Cursor, Lovable or Bolt
I'm not a developer, so please explain what you change in simple words. My app uses Supabase through supabase-js (the Data API). Supabase announced that from October 30, 2026, new tables in the public schema are no longer exposed to the Data API automatically. A new table returns "permission denied" (error 42501) until it has explicit GRANT statements.

Please:
1) List every table my app reads or writes through supabase-js, and say which ones were created recently or are created by my migrations.
2) For each table, work out which role needs which privilege: anon (visitors who are not logged in), authenticated (logged-in users) and service_role (server code only). Only grant what the app really uses. Do not grant anything to anon unless logged-out visitors truly need it, and never grant insert, update or delete to anon unless I confirm it.
3) Write a migration that, for each new table, does three things together: the GRANT statements, "alter table ... enable row level security", and real RLS policies that limit rows to the right user (for example using (auth.uid() = user_id)). Never use using (true) or with check (true) on insert, update or delete.
4) Add the same pattern to my project instructions, so every future table gets its grants, RLS and policies in the same migration.
5) Do not run "grant all" or "grant ... on all tables in schema public" to anon, and do not change existing tables unless I ask.

Make the smallest safe change, don't break existing features, and don't delete any data. When you're done, tell me in plain English what you changed and anything I need to do by hand in the Supabase dashboard.

7. Optional: adopt the new behavior before October 30

Supabase says you can opt in on an existing project now by running these statements in the SQL Editor. They change the defaults for future tables and sequences that the postgres role creates in public. Existing objects keep their grants.

alter default privileges for role postgres in schema public
  revoke select, insert, update, delete on tables from anon, authenticated, service_role;

alter default privileges for role postgres in schema public
  revoke usage, select on sequences from anon, authenticated, service_role;

To stop an existing table from being reachable through the Data API, Supabase shows a per-table revoke. Check that your app does not use the table first:

revoke all on table public.your_table from anon, authenticated, service_role;

The changelog also explains how to roll back if you opt in early and something breaks. The Data API exposure badge in the Table Editor and the Security Advisor list the tables worth reviewing.

8. Check what is exposed today

This change only affects new tables. Your existing tables keep their grants, so it is still worth checking that each one has RLS turned on with real policies. See our guide on checking Supabase Row Level Security. A Cheqvo repo scan flags tables without RLS and policies that allow everyone in your SQL migrations. It does not check your GRANT statements. Like any automated check, it can miss issues.

Quick answers

What exactly changes on October 30, 2026?

Supabase says that on October 30, 2026 the new behavior is applied to all existing projects. After that, a new table you create in the public schema is not reachable through the Data API (REST and GraphQL, which supabase-js uses) until you add an explicit GRANT for the roles that need it.

Will my existing tables stop working?

No. Supabase says existing tables keep their current grants and stay reachable. The change affects tables you create in the public schema after it reaches your project.

What does the error look like?

PostgREST returns error code 42501 with a message like "permission denied for table your_table", plus a hint that shows the GRANT statement for the role that is missing access.

Does this change Row Level Security?

No. Supabase says RLS behavior is unchanged. Grants control whether a role can reach a table at all. RLS controls which rows that role can see. You still need RLS turned on with real policies.

Am I affected if I only use a direct Postgres connection?

Supabase says no. If your app only talks to the database over a direct connection, such as psql, an ORM or an app server with a connection string, this change does not affect you.

Does it affect the auth, storage or realtime schemas?

No. Supabase says the change only touches default privileges in the public schema. Tables in storage, auth, realtime and custom schemas keep their current grants and defaults.

Should I just grant everything to anon to make the error go away?

No. That gives logged-out visitors access to the table, and if RLS is off or a policy is too loose, anyone with your public key could read or change data. Grant only the privileges each role needs, and keep RLS on with real policies.

Check your app now, free

Paste your live app's URL (like myapp.lovable.app) or a GitHub repo. You get a free score in under a minute, with no signup. The $19 Full Report gives a copy-paste AI fix for each issue found, and you get a full refund if the scan fails.

No need to type https://. Code on GitHub?

Want to see one first? Here is a real free result for OWASP Juice Shop, a public demo app.

Only scan repos and sites you own or are authorized to test. See our Terms.

No signup · read-only · we never keep your code. How we handle your code

No signup. We only read. We never run your code or attack your site. We never keep your code, only your report. Reports are saved so you can track progress over time. Delete anytime on request. See the leaderboard

General guidance. A fast automated check for the most common security mistakes in AI-built apps. Not a substitute for a full security audit. Updated 2026-10-26.