[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_cd800 のユーザーのみ全件参照できるように変更した。

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を許可する

  1. Supabase Dashboard にアクセス
  2. 対象プロジェクトを選択
  3. SettingsDatabaseNetwork Restrictions を開く
  4. 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条件を一致させることが重要
  • ポリシー変更はマイグレーションで管理するのが望ましい

参考リンク