[Supabase] 「データが一部しか取得できない」問題(RLSのUSING条件が原因)
問題:テーブルに5件あるのに、UIでは3件しか表示されない
Supabase(supabase-js)から onidazo テーブルを取得したところ、登録件数は5件あるはずなのに、UIには3件しか表示されなかった。
エラーは出ておらず、APIリクエスト自体は成功しているように見える。
ブラウザのネットワークタブでも、以下のように 200 OK が返っていた。
GET https://<project>.supabase.co/rest/v1/public.onidazo?select=... 200 (OK)それでも件数が合わないため、 RLS(Row Level Security) の影響を疑った。
環境
- Supabase(Postgres)
- supabase-js
- React(フロントエンド)
- RLS(Row Level Security)有効
- schema:
public - table:
onidazo
原因:RLSのUSING条件で行が自動的に除外されていた
RLSは「見せてよい行だけを返す」仕組みであり、条件に一致しない行はエラーを出さずに静かに除外される。
今回のSELECTポリシーは以下の条件になっており、ログイン中ユーザーの user_id と一致する行しか参照できなかった。
using (auth.uid() = user_id)その結果、テーブルに5件存在していても自分に紐づく3件しか取得できなかった。
解決手順
1. 現状のRLSポリシーを確認する
SupabaseのSQL Editorで pg_policies を見ると、設定済みのポリシーと条件(USING / WITH CHECK)を確認できる。
select
schemaname,
tablename,
policyname,
roles,
cmd,
qual as using_expression,
with_check as with_check_expression
from pg_policies
where schemaname = 'public'
and tablename = 'onidazo'
order by policyname;実行結果(例):
| schemaname | tablename | policyname | roles | cmd | using_expression | with_check_expression |
|---|---|---|---|---|---|---|
| public | onidazo | onidazo_select_authenticated | {authenticated} |
SELECT | (auth.uid() = user_id) |
null |
この結果から、authenticated ロールのSELECTは user_id が一致する行しか見えないことが分かった。
2. RLSにおけるUSINGとWITH CHECK
RLSは、テーブル単位で行レベルのアクセス制御を行う仕組み。
- RLSを有効化すると、デフォルトで全操作が拒否される
- 明示的にポリシーを定義しないと表示されない
- SELECTは
USING条件でフィルタされる
USINGとWITH CHECKの違い:
| 条件 | 説明 |
|---|---|
USING |
既存行を参照できるかどうか(SELECT/UPDATE/DELETE) |
WITH CHECK |
INSERT/UPDATE後の行が条件を満たすか |
USINGはWHEREのように動作する
例えば以下のポリシーがある場合:
using (auth.uid() = user_id)クエリ:
select * from onidazo;内部的には以下のように変換される:
select * from onidazo
where auth.uid() = user_id;つまり、USING 条件は自動的にクエリにフィルタとして追加される。
そのため:
- クエリは成功する(エラーにならない)
- 条件外の行は返らない
- 件数が減っても気づきにくい
件数が減っても気づきにくい
これが今回の原因だった。
3. ポリシー修正:権限グループ800のユーザーのみ全件取得できるようにする
UI側の制御(visibleAuthGroups: [800])と整合させるため、oni_member_authgrp_cd が 800 のユーザーのみ全件参照できるように変更した。
alter policy "onidazo_select_authenticated"
on public.onidazo
to authenticated
using (
exists (
select 1
from public.oni_member
where oni_member.uuid = auth.uid()
and oni_member.oni_member_authgrp_cd = 800
)
);必要であればDELETEも同条件にする。
alter policy "onidazo_delete_authenticated"
on public.onidazo
to authenticated
using (
exists (
select 1
from public.oni_member
where oni_member.uuid = auth.uid()
and oni_member.oni_member_authgrp_cd = 800
)
);※このポリシーは「権限グループ800のユーザーが全件閲覧可能になる」ため、運用上の権限設計には注意が必要。
動作確認
- 権限グループ800のユーザー → 全件取得
- それ以外のユーザー → 非表示(もしくは0件)
おまけ:supabase db pushで接続エラーが出た
マイグレーションを適用しようとして supabase db push を実行したところ、以下のエラーが発生した。
server error (FATAL: Address not in tenant allow_list: {123,456,789,000} (SQLSTATE XX000))原因:Network Restrictions(IP制限)
Supabase側でIP制限が有効な場合、CLI実行元のIPが許可されていないと接続できない。
解決方法1:SupabaseダッシュボードでIPを許可する
- Supabase Dashboard にアクセス
- 対象プロジェクトを選択
- Settings → Database → Network Restrictions を開く
- Add IP address で現在のIPアドレスを追加(または Add my current IP をクリック)
解決方法2:SQL Editorで直接ポリシーを変更する
IPアドレス制限を変更できない場合は、SupabaseのダッシュボードにあるSQL Editorから直接SQLを実行することでRLSポリシーを変更できる。
alter policy "onidazo_select_authenticated"
on public.onidazo
to authenticated
using (
exists (
select 1
from public.oni_member
where oni_member.uuid = auth.uid()
and oni_member.oni_member_authgrp_cd = 800
)
);この方法であればIPアドレス制限の影響を受けずにポリシーを変更できる。 ただし、この方法ではマイグレーションファイルが保存されないため、管理面では十分とは言えない。 また、IPアドレスは通常変動するため、安定した運用を行うには固定IPの導入を検討する必要がある。
まとめ
- RLSのUSING条件はSELECTに対して 暗黙のWHERE として動作する
- 件数が減ってもエラーにならないため気づきにくい
- UI側の権限仕様とRLS条件を一致させることが重要
- ポリシー変更はマイグレーションで管理するのが望ましい