<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: dave thomas</title>
    <description>The latest articles on DEV Community by dave thomas (@dave_thomas_18311ddb9c409).</description>
    <link>https://dev.to/dave_thomas_18311ddb9c409</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4196847%2Ffcd797ff-8870-4f74-a01f-8a737d67d90a.jpg</url>
      <title>DEV Community: dave thomas</title>
      <link>https://dev.to/dave_thomas_18311ddb9c409</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dave_thomas_18311ddb9c409"/>
    <language>en</language>
    <item>
      <title>Supabase's Oct 30 change: what breaks, what doesn't, and the quick fix to avoid</title>
      <dc:creator>dave thomas</dc:creator>
      <pubDate>Tue, 20 Oct 2026 20:22:53 +0000</pubDate>
      <link>https://dev.to/dave_thomas_18311ddb9c409/supabases-oct-30-change-what-breaks-what-doesnt-and-the-quick-fix-to-avoid-bg4</link>
      <guid>https://dev.to/dave_thomas_18311ddb9c409/supabases-oct-30-change-what-breaks-what-doesnt-and-the-quick-fix-to-avoid-bg4</guid>
      <description>&lt;p&gt;On October 30, 2026, Supabase applies a new default to all existing projects: new tables in the public schema are not exposed to the Data API automatically [S1]. "Data API" means the auto-generated REST and GraphQL layer, which is what supabase-js and the other client libraries call.&lt;br&gt;
This post covers what changes, who it affects, how to check your own project with SQL, the safe way to expose a table, and the quick fix that can quietly re-expose your data.&lt;br&gt;
What changes&lt;br&gt;
Until now, a Supabase project with default settings granted select, insert, update and delete on every table in public to the anon, authenticated and service_role roles, through Postgres default privileges. Every table you created was reachable through the API the moment it existed [S1].&lt;br&gt;
After the change [S1]:&lt;br&gt;
Step&lt;br&gt;
Before&lt;br&gt;
After&lt;br&gt;
Create a table in public&lt;br&gt;
Reachable through the Data API on creation&lt;br&gt;
Exists, but the Data API can't reach it&lt;br&gt;
Grants to anon / authenticated / service_role&lt;br&gt;
Implicit, via default privileges&lt;br&gt;
You must run an explicit GRANT&lt;br&gt;
enable row level security / create policy&lt;br&gt;
Unchanged&lt;br&gt;
Unchanged&lt;br&gt;
What stays the same [S1]:&lt;br&gt;
Existing tables keep their current grants and stay reachable.&lt;br&gt;
RLS behaves the same. Grants are a separate layer.&lt;br&gt;
Direct Postgres connections (psql, an ORM, or an app server with a connection string) aren't affected.&lt;br&gt;
The change touches default privileges in public only. storage, auth, realtime and custom schemas keep their current grants and defaults.&lt;br&gt;
When a grant is missing, the API returns an error instead of failing silently [S1]:&lt;br&gt;
{&lt;br&gt;
  "code": "42501",&lt;br&gt;
  "message": "permission denied for table your_table",&lt;br&gt;
  "hint": "Grant the required privileges to the current role with: GRANT SELECT ON public.your_table TO anon;"&lt;br&gt;
}&lt;br&gt;
Timeline [S1]:&lt;br&gt;
Apr 28, 2026: changelog published, and the opt-in setting appeared at project creation.&lt;br&gt;
May 30, 2026: the new behavior became the default for new projects, rolled out gradually.&lt;br&gt;
Oct 30, 2026: enforced on all existing projects.&lt;br&gt;
Supabase emails owners and admins of active projects: a five-week notice went out Sep 23 and a final notice goes out Oct 23.&lt;br&gt;
Until Oct 30, Security Advisor flags affected tables and shows the SQL to fix them.&lt;br&gt;
Who is affected&lt;br&gt;
According to Supabase [S1], you need to act if any of these is true:&lt;br&gt;
Your app reads or writes public tables through the Data API (PostgREST, GraphQL, supabase-js or another client library, or direct HTTP to /rest/v1/ or /graphql/v1).&lt;br&gt;
Your migrations or provisioning flow create tables in public without explicit GRANT statements.&lt;br&gt;
Your AI coding tool, CLI script or Management API call creates tables and expects them to be reachable through the API right away.&lt;br&gt;
A project with no flagged tables today still breaks the first time someone adds a new table without a grant [S1].&lt;br&gt;
Supabase's stated reason matters for anyone building with AI tools. Agents, CLI scripts and AI platforms now create tables, often with no human reviewing the diff. The old defaults made it easy to expose a new table before anyone had secured it [S1].&lt;br&gt;
How to check your own project&lt;br&gt;
Start with the built-in tools: Security Advisor and the Data API exposure badge in the Table Editor list the tables worth reviewing [S1].&lt;br&gt;
To see the details yourself, run the queries below in the SQL Editor. They only read the system catalogs and change nothing.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;RLS status of every table in public
select tablename, rowsecurity as rls_enabled
from pg_tables
where schemaname = 'public'
order by rls_enabled, tablename;&lt;/li&gt;
&lt;li&gt;Which API roles hold which privileges on which tables
select table_name,
   grantee,
   string_agg(privilege_type, ', ' order by privilege_type) as privileges
from information_schema.role_table_grants
where table_schema = 'public'
and grantee in ('anon', 'authenticated', 'service_role')
group by table_name, grantee
order by table_name, grantee;&lt;/li&gt;
&lt;li&gt;The dangerous combination: the API can read the table AND RLS is off
select c.relname as table_name,
   case c.relkind when 'r' then 'table' when 'p' then 'partitioned table'
                  when 'v' then 'view' when 'm' then 'materialized view' end as kind,
   has_table_privilege('anon', c.oid, 'select')          as anon_can_select,
   has_table_privilege('authenticated', c.oid, 'select') as authenticated_can_select,
   c.relrowsecurity                                      as rls_enabled
from pg_class c
join pg_namespace n on n.oid = c.relnamespace
where n.nspname = 'public'
and c.relkind in ('r', 'p', 'v', 'm')
and not c.relrowsecurity
and (has_table_privilege('anon', c.oid, 'select')
   or has_table_privilege('authenticated', c.oid, 'select'))
order by table_name;
Any row here can be read through the API by that role, with no row filtering. For anon, that means anyone holding your public anon key, and that key ships in your frontend JavaScript by design. Views and materialized views show up here too, because RLS isn't enabled on them directly; review them separately.&lt;/li&gt;
&lt;li&gt;Your current default privileges (what future tables will get)
select pg_get_userbyid(d.defaclrole)  as for_role,
   d.defaclnamespace::regnamespace as schema,
   case d.defaclobjtype when 'r' then 'tables' when 'S' then 'sequences'
                        when 'f' then 'functions' else d.defaclobjtype::text end as object_type,
   d.defaclacl                     as default_acl
from pg_default_acl d
order by schema, for_role, object_type;&lt;/li&gt;
&lt;li&gt;Policies that let everyone through
select tablename, policyname, roles, cmd, qual, with_check
from pg_policies
where schemaname = 'public'
and (qual = 'true' or with_check = 'true')
order by tablename;
A policy of using (true) has RLS turned on but filters no rows. Make sure that's what you intend for each table listed.
The safe pattern: minimal grants plus RLS, as one unit
Supabase recommends treating three steps as one unit, in the same migration [S1]:
-- 1. Grant only the privileges each role needs
grant select on public.your_table to anon;                                   -- only if logged-out users should read it
grant select, insert, update, delete on public.your_table to authenticated;
grant select, insert, update, delete on public.your_table to service_role;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;-- 2. Enable RLS&lt;br&gt;
alter table public.your_table enable row level security;&lt;/p&gt;

&lt;p&gt;-- 3. Add the policies you need&lt;br&gt;
create policy "users can read their own rows"&lt;br&gt;
  on public.your_table&lt;br&gt;
  for select&lt;br&gt;
  to authenticated&lt;br&gt;
  using (auth.uid() = user_id);&lt;br&gt;
These come from the Securing your API guide [S2]:&lt;br&gt;
Grants decide which roles can reach a table; RLS decides which rows they see. Use both.&lt;br&gt;
If anon shouldn't touch a table at all, don't grant it anything. That's stricter than relying on a policy alone.&lt;br&gt;
Tables created in the Dashboard have RLS on by default. Tables created with SQL or by tools may not, so check query 1.&lt;br&gt;
RLS doesn't apply to functions. Grant EXECUTE carefully, and review any SECURITY DEFINER functions you expose.&lt;br&gt;
If your AI coding tool creates tables, update its system prompt (or use Supabase's agent skill) so grants, RLS and policies always ship together [S1]. The 42501 hint is also written so an agent can correct itself when a grant is missing [S1].&lt;br&gt;
The risky quick fix&lt;br&gt;
The Supabase FAQ includes a rollback for people who opted in early and broke their app [S1]. It restores the old default privileges and then grants in bulk:&lt;br&gt;
grant select, insert, update, delete on all tables in schema public to anon, authenticated, service_role;&lt;br&gt;
grant usage, select on all sequences in schema public to anon, authenticated, service_role;&lt;br&gt;
Supabase documents this rollback, and it works. It also returns you to the old behavior, where every table is exposed, including tables you meant to keep private or never protected with RLS. Supabase's own note says to revoke private tables individually afterwards [S1]:&lt;br&gt;
revoke all on table public.your_private_table from anon, authenticated, service_role;&lt;br&gt;
Expect people to copy the bulk grant from a forum or from their AI assistant on Oct 31, because it makes the 42501 errors go away. If you do use it, run queries 3 and 5 immediately afterwards and fix every row they return.&lt;br&gt;
A better approach: grant per table, for the roles that need it, in the same migration that enables RLS and adds policies.&lt;br&gt;
A short checklist before Oct 30&lt;br&gt;
Open Security Advisor and the Table Editor exposure badge [S1].&lt;br&gt;
Run queries 1 and 3. Every table the API can read should have RLS on and real policies.&lt;br&gt;
Add explicit GRANT statements to the migrations or prompts that create tables [S1].&lt;br&gt;
Optional: opt in early on a staging copy using the revoke statements in the changelog, then test your app [S1].&lt;br&gt;
After any bulk grant, run queries 3 and 5 again.&lt;br&gt;
I'm Dave. I built Vibe Greenlight with my AI agent, Eric. It's a launch check for AI-built apps. If you want an outside view of your live app, there's a free mini-scan that checks your homepage for exposed keys and files, with no repo access needed. The paid Launch Check adds a read-only probe for Supabase tables your anon key can read: vibegreenlight.com. It's an automated check, not a pentest, and it doesn't replace running the queries above.&lt;br&gt;
Sources&lt;br&gt;
Supabase changelog: &lt;a href="https://supabase.com/changelog/45329-breaking-change-tables-not-exposed-to-data-and-graphql-api-automatically" rel="noopener noreferrer"&gt;https://supabase.com/changelog/45329-breaking-change-tables-not-exposed-to-data-and-graphql-api-automatically&lt;/a&gt;&lt;br&gt;
Supabase docs, Securing your API: &lt;a href="https://supabase.com/docs/guides/api/securing-your-api" rel="noopener noreferrer"&gt;https://supabase.com/docs/guides/api/securing-your-api&lt;/a&gt;&lt;/p&gt;

</description>
      <category>supabase</category>
      <category>webdev</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
