DEV Community

Cover image for Supabase Data API exposure changes on 30 October: grant tables explicitly
Dave Kurian
Dave Kurian

Posted on Originally published at otf-kit.dev

Supabase Data API exposure changes on 30 October: grant tables explicitly

On a project with the new default, a table you just created is invisible to the Data API until someone grants it. Your app gets an error where it used to get rows, while the table sits in your database looking fine. Supabase announced the change on 28 April 2026, and it reaches every existing project on 30 October 2026.

This post covers the exposure change only: what changed, the dates, the grants to add, and how to check them before the 30th. For the policy side, see the row-level security production checklist.

What changed

Until now, a new table in the public schema was reachable through the Data API as soon as it existed. The Supabase changelog states the new rule in one line: "New tables in the public schema will no longer be exposed to the Data API automatically." A new table now needs an explicit Postgres grant before the Data API can see it.

The changelog gives three dates:

  • 28 April 2026: the new setting is available for new projects, at project creation.
  • 30 May 2026: it becomes the default for new projects, rolled out over a few weeks.
  • 30 October 2026: it is applied to all existing projects.

Existing tables are not touched. The changelog says they "keep their current grants and stay reachable." The change matters most for tables you create from now on, and for any project that moves to the new default.

If your app only uses a direct Postgres connection, such as an ORM, psql or a server with a connection string, the changelog says it is unaffected. The change is about the Data API, which supabase-js and the other client libraries call over HTTP.

Why a table goes missing

The Data API docs put the rule plainly: "A table isn't reachable through the Data API unless you have granted a role privileges on it." A missing grant returns a 42501 error from PostgREST.

On existing projects today, new tables in public get select, insert, update and delete for anon, authenticated and service_role by default. The docs say Supabase is "changing the platform default to revoke these automatic grants so that exposure becomes opt-in." That default is the thing you need to plan around.

1. Find the tables the Data API can reach

Run this in the SQL Editor. It is our query, not Supabase's, so check the output against your own schema:

select table_name, grantee, privilege_type
from information_schema.role_table_grants
where table_schema = 'public'
  and grantee in ('anon', 'authenticated', 'service_role')
order by table_name, grantee;
Enter fullscreen mode Exit fullscreen mode

A table your app needs that is missing from the output has no grants, so it stays unreachable until you add them. Also open the Security Advisor. The changelog says it will flag affected tables before the date, and that owners and admins will be emailed.

A three-by-three grid of table cards, with one card dimmed

2. Grant each role the minimum

The docs recommend the smallest grant that works: read-only for anonymous clients, full access for signed-in users with RLS still applied, and full access for the service role only where server code reaches the table through the Data API.

-- Read-only access for anonymous clients
grant select on table public.your_table to anon;

-- Full access for signed-in users; RLS still applies
grant select, insert, update, delete on table public.your_table to authenticated;

-- Full access for server-side code using the service role
grant select, insert, update, delete on table public.your_table to service_role;
Enter fullscreen mode Exit fullscreen mode

Our rule is no write grant to anon unless anonymous writes are the point of the table.

3. Turn on row level security

The docs say tables created through the Supabase Dashboard have RLS on by default. Tables created in the SQL Editor or with another tool do not, so enable it yourself:

alter table public.your_table enable row level security;
Enter fullscreen mode Exit fullscreen mode

The docs warn that "Tables and views exposed through the Data API without RLS can be accessed by any role with matching grants." A grant lets a role in, and RLS decides which rows it sees. The docs also note that RLS does not apply to functions, so grant EXECUTE only to the roles that need it.

A padlock on a glowing plate on the left, and a golden key on a plate on the right

4. Choose grant-per-table or opt in early

You have two routes. The first is the grant in step 2, table by table. The second is to revoke the defaults, so every new table starts closed. The docs give the statements:

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 execute on functions from anon, authenticated, service_role;

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

alter default privileges for role postgres in schema public
  revoke execute on functions from public;
Enter fullscreen mode Exit fullscreen mode

The changelog says running the revoke statements the dashboard runs at project creation adopts the new behavior on an existing project today. The docs list the statements above, but we could not confirm they match the dashboard's exactly, so compare them before you run anything. Opting in is optional before the 30th. Our reading is that default privileges apply to objects created after you run them, so tables that already exist keep their grants. Run the statements on a staging project first. After that, every new table needs its grants in the same migration that creates it, or your app will not see it.

5. Test each role

Call your own endpoints three ways: with the anon key, with a signed-in user's token, and with the service role key from server code. Each request should return what that role is meant to see.

Our reading of the two errors you will meet: a 42501 on a table the app needs means a grant is missing. An empty result from a table you expected rows from usually points to a policy that filters every row, not to the grant.

Work backward from 30 October

Give yourself at least a week. Run the query in step 1 and list the tables your app reads and writes. Put the grants and the RLS policies into a migration, and test that migration on staging with all three roles. Run the opt-in statements on staging, then on production, and check the Security Advisor the next day.

What we could not confirm

  • The docs do not say whether the default has already taken effect on a given project. Check your project's Data API settings and the tables it exposes, rather than relying on the date alone.
  • The changelog gives the dates but no per-project migration steps. The Security Advisor and the emails are the only signals it names.
  • We have not run these steps against a live Supabase project. Test the SQL on staging before you run it on production.

Sources

Top comments (0)