<?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: Amichay Ilan</title>
    <description>The latest articles on DEV Community by Amichay Ilan (@amichayilan).</description>
    <link>https://dev.to/amichayilan</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%2F4174277%2Fd714182d-eec1-48da-93fd-a2a223d529cb.png</url>
      <title>DEV Community: Amichay Ilan</title>
      <link>https://dev.to/amichayilan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/amichayilan"/>
    <language>en</language>
    <item>
      <title>Supabase's October 30 change: fix error 42501 without opening your data</title>
      <dc:creator>Amichay Ilan</dc:creator>
      <pubDate>Fri, 09 Oct 2026 22:07:10 +0000</pubDate>
      <link>https://dev.to/amichayilan/supabases-october-30-change-fix-error-42501-without-opening-your-data-5fk6</link>
      <guid>https://dev.to/amichayilan/supabases-october-30-change-fix-error-42501-without-opening-your-data-5fk6</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  What changes
&lt;/h2&gt;

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

&lt;p&gt;Source: &lt;a href="https://supabase.com/changelog/45329-breaking-change-tables-not-exposed-to-data-and-graphql-api-automatically" rel="noopener noreferrer"&gt;Supabase changelog&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What you'll see
&lt;/h2&gt;

&lt;p&gt;The first query against a new table fails with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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;"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The trap
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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 &lt;code&gt;anon&lt;/code&gt; 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.&lt;/p&gt;

&lt;h2&gt;
  
  
  The safe fix
&lt;/h2&gt;

&lt;p&gt;Do all three steps in the same migration. Replace &lt;code&gt;your_table&lt;/code&gt; and &lt;code&gt;user_id&lt;/code&gt; with your table and its owner column.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="c1"&gt;-- 1. Grant only what the app needs&lt;/span&gt;
&lt;span class="k"&gt;grant&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;your_table&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- Server code (Edge Functions) that uses the service role needs it too:&lt;/span&gt;
&lt;span class="k"&gt;grant&lt;/span&gt; &lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;update&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;your_table&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;service_role&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;-- Grant select to anon only if logged-out visitors must read this table.&lt;/span&gt;

&lt;span class="c1"&gt;-- 2. Turn on row-level security&lt;/span&gt;
&lt;span class="k"&gt;alter&lt;/span&gt; &lt;span class="k"&gt;table&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;your_table&lt;/span&gt; &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="k"&gt;row&lt;/span&gt; &lt;span class="k"&gt;level&lt;/span&gt; &lt;span class="k"&gt;security&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;-- 3. Let each user reach only their own rows&lt;/span&gt;
&lt;span class="k"&gt;create&lt;/span&gt; &lt;span class="n"&gt;policy&lt;/span&gt; &lt;span class="nv"&gt;"owners manage their rows"&lt;/span&gt;
  &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="k"&gt;public&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;your_table&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="k"&gt;all&lt;/span&gt;
  &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="n"&gt;authenticated&lt;/span&gt;
  &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="k"&gt;check&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uid&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;h2&gt;
  
  
  Check the tables you already have
&lt;/h2&gt;

&lt;p&gt;It takes two minutes. Run this in the Supabase SQL Editor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;select&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relname&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="k"&gt;table_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relrowsecurity&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;rls_enabled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;has_table_privilege&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'anon'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'select'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;anon_can_read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="n"&gt;has_table_privilege&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'authenticated'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;oid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'select'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;signed_in_can_read&lt;/span&gt;
&lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pg_class&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relnamespace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'public'&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;regnamespace&lt;/span&gt;
  &lt;span class="k"&gt;and&lt;/span&gt; &lt;span class="k"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;relkind&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'r'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'p'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;order&lt;/span&gt; &lt;span class="k"&gt;by&lt;/span&gt; &lt;span class="n"&gt;rls_enabled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;table_name&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;rls_enabled&lt;/code&gt; false and &lt;code&gt;anon_can_read&lt;/code&gt; true: anyone with your site's public key can read every row.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rls_enabled&lt;/code&gt; false and &lt;code&gt;signed_in_can_read&lt;/code&gt; true: anyone who signs up can read every row.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rls_enabled&lt;/code&gt; true: the policies decide. Make sure none of them say &lt;code&gt;using (true)&lt;/code&gt; on user data.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;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.&lt;/p&gt;

</description>
      <category>supabase</category>
      <category>postgres</category>
      <category>security</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
