「Before」と「After」で、Supabase Data API のテーブル公開方法の変更を示す図。これまで自動公開されていたスキーマのテーブルが、明示的なGRANTで権限を付与するようになる移行プロセスとSQLコードが描かれている。

Supabase が Data API のデフォルト動作を変更します。2026年5月30日から新規プロジェクト、10月30日から既存プロジェクトで、public スキーマに作成したテーブルが Data API に自動公開されなくなります

「え、今まで自動公開だったの?」と思った方——そうです。Supabase はこれまで public スキーマのテーブルを PostgREST(Data API)と GraphQL API に自動的に露出させていました。RLS(Row Level Security)で行レベルの制御はできましたが、テーブルの存在自体は API から見える状態でした。

この記事では、変更の全容と、既存プロジェクトを明示的 GRANT に移行する具体的な手順を紹介します。

何が変わるのか

Before:暗黙の全公開

これまでの Supabase は、public スキーマにテーブルを作成すると、anonauthenticatedservice_role の3ロールに対して SELECT, INSERT, UPDATE, DELETE が自動的に付与されていました。

-- 今までの暗黙の動作(ユーザーが書かなくても裏で実行されていた)
alter default privileges in schema public
  grant all on tables to anon, authenticated, service_role;

つまり CREATE TABLE した瞬間に Data API から叩ける状態です。

After:明示的 GRANT が必須

5月30日以降の新規プロジェクト(10月30日以降は全プロジェクト)では、テーブルを作成しただけでは Data API に露出しません。GRANT 文で明示的にアクセスを許可する必要があります。

create table public.posts (
  id uuid primary key default gen_random_uuid(),
  user_id uuid references auth.users(id) not null,
  title text not null,
  body text,
  created_at timestamptz default now()
);

-- これがないと Data API から見えない
grant select on public.posts to anon;
grant select, insert, update, delete on public.posts to authenticated;

alter table public.posts enable row level security;

create policy "自分の投稿のみ操作可能"
  on public.posts
  for all
  to authenticated
  using (auth.uid() = user_id);

GRANT を書き忘れると、PostgREST はエラーコード 42501 を返します。エラーメッセージには修正に必要な SQL が含まれるので、見落としても気づけます。

ロールアウトの4段階

日付 内容
4月28日 プロジェクト作成時にオプトイン可能(「自動公開」チェックボックス)
5月18日 pg_graphql が新規プロジェクトでデフォルト無効に
5月30日 新規プロジェクトで自動公開がデフォルトOFF
10月30日 既存プロジェクトにも適用

既存テーブルの GRANT は剥奪されません。影響を受けるのは適用日以降に作成する新しいテーブルです。

なぜこの変更が必要なのか

1. 暗黙の公開は事故のもと

RLS を設定する前のわずかな時間でも、テーブルは API 経由で読み書きできる状態でした。開発中に CREATE TABLE してから RLS ポリシーを書くまでの間、データが露出するリスクがあります。

2. 宣言的コードとの親和性

明示的な GRANT 文はマイグレーションファイルに残ります。reviewablediffablegreppable——コードレビューで権限の変更を追跡できるようになります。

3. ロール別の権限が可視化される

anon(未認証)と authenticated(認証済み)では必要な権限が異なります。明示的 GRANT なら、その違いがマイグレーションファイルの中で一目瞭然です。

-- anon には SELECT のみ(公開情報の閲覧だけ)
grant select on public.posts to anon;

-- authenticated には CRUD を許可(RLS で行制御)
grant select, insert, update, delete on public.posts to authenticated;

4. AI コーディングツールとの相性

Cursor、Claude Code、GitHub Copilot——AI ツールがマイグレーションを自動生成する時代です。暗黙の公開に依存していると、AI が CREATE TABLE だけ生成して GRANT を忘れるケースが起きます。明示的 GRANT が必須になれば、AI ツールも(そしてそのプロンプトも)正しいパターンを学習します。

Supabase は Agent Skills というオープンソースの指示セットを公開しており、Claude Code や Copilot に正しいパターンを教えることができます。

実プロジェクトの Before / After

実際に運用中のプロジェクトで、どう変わるかを見てみます。

Before:grant all パターン(現在の多くのプロジェクト)

-- 初期マイグレーションでよく見るパターン
grant all on all tables in schema public
  to anon, authenticated, service_role, postgres;

alter default privileges in schema public
  grant all on tables
  to anon, authenticated, service_role, postgres;

この1行で全テーブルが全ロールに全権限公開されます。便利ですが、anon ロールに DELETE 権限が付いているのは過剰です。

After:最小権限パターン(移行後)

-- テーブルごとに必要な権限だけを付与
grant select on public.posts to anon;
grant select, insert, update, delete on public.posts to authenticated;
grant select, insert, update, delete on public.posts to service_role;

grant select on public.profiles to anon;
grant select, update on public.profiles to authenticated;
grant select, insert, update, delete on public.profiles to service_role;

テーブルごとにロール×操作の組み合わせを明示します。コード量は増えますが、「何が公開されているか」が grep 一発で分かります。

# どのテーブルが anon に公開されているか一覧
grep -r "grant.*to anon" supabase/migrations/

移行の手順

Step 1:現状を監査する

Supabase ダッシュボードの Security Advisor で、現在どのテーブルがどのロールに公開されているかを確認します。テーブルエディターには「Data API exposure」バッジも表示されるようになっています。

SQL で直接確認することもできます。

select
  grantee,
  table_name,
  string_agg(privilege_type, ', ' order by privilege_type) as privileges
from information_schema.table_privileges
where table_schema = 'public'
  and grantee in ('anon', 'authenticated', 'service_role')
group by grantee, table_name
order by table_name, grantee;

Step 2:デフォルト権限を取り消す

既存プロジェクトで早期に移行する場合、まずデフォルトの自動付与を止めます。

-- 新しいテーブルへの自動 GRANT を停止
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;

このマイグレーションを適用すると、以降に作成するテーブルは明示的 GRANT がないと Data API に露出しなくなります。既存テーブルの権限はそのまま残ります。

Step 3:テーブルごとの GRANT をマイグレーションに追加

既存テーブルの権限を最小化したい場合は、現在の grant all を取り消してからテーブルごとに再付与します。

-- まず全部取り消す
revoke all on all tables in schema public
  from anon, authenticated;

-- テーブルごとに再付与
grant select on public.posts to anon;
grant select, insert, update, delete on public.posts to authenticated;

grant select on public.profiles to anon;
grant select, update on public.profiles to authenticated;

-- 内部テーブルは authenticated にも公開しない
-- (service_role のみ、または RPC 関数経由でアクセス)

Step 4:ローカル開発環境を検証する

supabase db reset でマイグレーションを最初から再生し、アプリケーションが正常に動作することを確認します。GRANT がマイグレーションに含まれていれば、ローカルでも本番でも同じ権限構成が再現されます。

supabase db reset
npm run dev
# アプリの主要機能を手動テスト

Step 5:CI で権限の整合性を保証する

マイグレーションの diff に CREATE TABLE があるのに GRANT がない——これを CI で検知できると安心です。

#!/bin/bash
# pre-commit hook or CI step
for migration in supabase/migrations/*.sql; do
  tables=$(grep -oP 'create table (?:if not exists )?public\.(\w+)' "$migration" | \
           grep -oP 'public\.\w+')
  for table in $tables; do
    if ! grep -q "grant.*on ${table}" "$migration"; then
      echo "WARNING: ${migration} creates ${table} but has no GRANT statement"
    fi
  done
done

2層セキュリティモデルの再確認

この変更を機に、Supabase のセキュリティモデルを整理しておきます。

Layer 1: GRANT  → テーブルに「アクセスできるか」を制御
Layer 2: RLS    → テーブルの「どの行を操作できるか」を制御

GRANT がなければ RLS の出番すらありません。逆に、GRANT があっても RLS が無効なら全行が見えます。両方セットで設計するのが正解です。

マイグレーションでは、テーブル作成・GRANT・RLS を1ファイルにまとめるのがベストプラクティスです。

-- 1ファイルに全部まとめる
create table public.comments (...);

grant select on public.comments to anon;
grant select, insert, update, delete on public.comments to authenticated;

alter table public.comments enable row level security;

create policy "..." on public.comments ...;

まとめ

項目 旧デフォルト 新デフォルト
テーブル公開 自動 明示的 GRANT が必要
GraphQL デフォルト有効 デフォルト無効
既存テーブル 影響なし 影響なし(新テーブルのみ)
移行期限 既存プロジェクトは 10月30日

やることは明確です。

  1. 今すぐ:Security Advisor で現状を監査する
  2. 5月30日まで:新規プロジェクトを作る予定があれば、GRANT パターンに慣れておく
  3. 10月30日まで:既存プロジェクトのマイグレーションに GRANT を追加する

「暗黙の全公開」から「明示的な最小権限」へ。地味な変更ですが、Supabase を本番で運用するなら避けて通れないアップデートです。マイグレーションファイルに権限が宣言的に残る——これはむしろ歓迎すべき変化だと思います。

参考リンク