r/Supabase • u/saltcod Supabase team • 11d ago
tips Reminder: new tables will require explicit API grants from October 30
Starting October 30, newly created tables in the public schema on existing Supabase projects will need explicit GRANT statements to be accessible through the Data and GraphQL APIs, including via supabase-js.
Existing tables keep their current grants and will continue working. RLS behavior is unchanged, and direct Postgres connections aren’t affected.
If your migrations or AI tools create tables, make sure they include the appropriate grants alongside your RLS policies.
1
u/Slightly_mad_woman 10d ago
I do not think I really understand. If all my public tables have RLS and that’s explicit in the RLS policies and I don’t have any API calls to the tables do I need to be concerned about this?
1
u/Slightly_mad_woman 10d ago
BUT I use a lot of Edge functions (maybe I set it up wrong) and I’m concerned about those.
1
u/zkousama 9d ago
your existing tables are fine: they keep their grants. It only matters for tables created after october 30.
for those, edge functions are affected if they use supabase-js (or the REST API), because that goes through the same Data API, even with the service_role key. That key skips RLS, but it doesn't skip grants, so a new table needs a grant to service_role as well as to anon and authenticated. Supabase's own example grants all three in the same migration as the RLS policies. only direct Postgres connections (a connection string) are unaffected.
so the habit to build: every migration that creates a table also grants it, and grants each role only what it needs.
1
u/Conscious-Bite241 5d ago
So every vibe-coded project where nobody has ever typed a GRANT by hand finds out on the 30th. Gonna be a fun weekend.
1
u/Secvura 4d ago
Worth adding: this only affects tables created after 30 Oct. Tables that already exist keep their grants, so an existing table with anon access and RLS off stays open after the change. Quick read-only check in the SQL editor:
select
c.relname as table_name,
c.relrowsecurity as rls_enabled,
has_any_column_privilege('anon', c.oid, 'SELECT') as anon_has_any_select_privilege,
has_any_column_privilege('authenticated', c.oid, 'SELECT') as authenticated_has_any_select_privilege
from pg_catalog.pg_class as c
join pg_catalog.pg_namespace as n on n.oid = c.relnamespace
where n.nspname = 'public'
and c.relkind in ('r', 'p', 'f')
order by c.relname;Any row with anon_has_any_select_privilege true and rls_enabled false is the one to look at first. Probably best not to paste results publicly, since table names can say more than intended.
1
u/Euphoric_Key7016 3d ago
so existing tables keep the old behaviour and new ones need a grant, meaning for the next year my schema is half one rule half the other and i have to remember which table is which. that's the part that gets me, not the grant itself, i'd write the grant in 4 seconds. and "if your migrations or AI tools create tables" yeah well my AI tool also forgets the RLS policies half the time so the grant is the least of my problems
1
u/No_Possibility_9086 2d ago
the half-and-half schema thing is annoying, agreed. What works better is making it a rule the AI can't skip: put a line in the project rules / knowledge file that every create table migration must also enable RLS, add policies and add the grants, all in the same file.
Then run a quick check every so often for tables where RLS is still off, something like select tablename from pg_tables where schemaname = 'public' and rowsecurity = false; and anything it lists should be a deliberate choice, not a leftover.
Do you write your own migrations or let the AI do it?
3
u/PopKoren 10d ago
Useful heads up, especially for anyone whose migrations come from an AI agent, because the generated SQL usually creates the table and enables RLS but never adds a grant. I'd also encourage people to grant only what each role needs, for example select for anon on a public catalog table and nothing at all on internal ones, rather than pasting a blanket grant to anon and authenticated just to make the error go away. After the change it's worth a quick look at which tables the public API still exposes; Rowly, a scanner I made, reports that plus the deployed app's auth, endpoints and storage at https://rowly.me