起きたこと
AIにSupabaseのデータ取得処理を書いてもらい、動作確認したところエラーは出ないのに一覧が空でした。
const { data, error } = await supabase
.from("posts")
.select("*")
.eq("user_id", userId);
if (error) {
console.error(error);
}
console.log(data); // => []
error は null、HTTPステータスは 200。それなのに data が空配列。テーブルには対象の行が実在しているにもかかわらず、です。
原因: RLS(Row Level Security)は「絞り込み」であって「エラー」ではない
SupabaseはPostgreSQLのRLS(行レベルセキュリティ)をそのまま使っています。RLSポリシーに合致しない行は、クエリの結果から静かに除外されるだけで、権限エラーにはなりません。
たとえば以下のようなポリシーだと、
create policy "select_own_posts"
on posts for select
using (auth.uid() = user_id);
user_id が今ログイン中のユーザーIDと一致する行しか返ってきません。RESTのステータスコード的には「正常に0件取得できた」扱いなので、error は発生しないのです。
実際に3パターン試した結果
同じテーブル・同じクエリで、認証状態だけを変えて挙動を確認しました。
| 状況 | ステータス | data | error |
|---|---|---|---|
| ポリシー通り自分の行を取得 | 200 | 該当行の配列 | null |
他人の行をuser_id指定で取得 |
200 |
[](空) |
null |
| 未ログイン(anonキー・RLS on) | 200 |
[](空) |
null |
| テーブル自体のRLSを有効化していない場合 | 200 | 全行 | null |
権限がない・条件に合わないケースが、すべて「200 + 空配列」に丸められてしまうのが分かります。これがAI生成コードの if (error) チェックだけでは検知できない理由です。
なお、RLSを有効化した上でポリシーを1つも設定していないテーブルは、Postgresのデフォルト動作により全件アクセス拒否(=常に0件)になります。上の表の「全行」は、RLS自体を有効化していないテーブルの場合の挙動なので混同しないよう注意してください。
AIへの頼み方をどう変えるか
悪い例
「Supabaseから投稿一覧を取得する処理を書いて」
これだと if (error) だけの雛形が返ってきがちです。RLSの絞り込みは想定に入りません。
良い例
「Supabaseから投稿一覧を取得する処理を書いてください。RLSポリシーにより意図せず0件になるケースを想定し、data が空配列だった場合はログにuser_idと一緒に警告を出すようにしてください」
こう頼むと、「0件=正常系だが要注意」という分岐をAIが最初から組み込んでくれます。
const { data, error } = await supabase
.from("posts")
.select("*")
.eq("user_id", userId);
if (error) {
console.error("[posts] fetch error", error);
} else if (data.length === 0) {
console.warn("[posts] 0 rows returned. RLS policy filtering? user_id=", userId);
}
まとめ
- SupabaseのRLSは、権限外の行をエラーではなく空の結果として返す
-
if (error)だけのチェックでは、RLSによる意図しない0件を見逃す - AIに頼むときは「0件になるケースを想定してログを出す」まで指定すると、原因追跡がしやすいコードになる
RLSの仕組み自体はSupabase公式ドキュメントの内容に沿っていますが、テーブル設計やポリシーの書き方によって挙動が変わる場合があります。導入時は公式ドキュメントも合わせてご確認ください。
なお、この内容はプログラミング学習を始めたばかりの方には少し高度かもしれません。中上級者の方は、初心者の知り合いへの紹介や社内研修の参考としてどうぞ。
未経験から Next.js + Supabase + Claude Code で Webアプリを公開するまで
全20セッションの教材付きプランで、面談なしで今日から学習を始められます。
- 教材完全版+メンタリング(月5,500円・いつでも解約OK)→ https://menta.work/plan/20251?ref=qiita
- 無料の教材体験版(最初の数セッション)→ https://github.com/ayies128/next-ai-camp-trial
- YouTube『AIエンジニア情報局』(別運営・AI×開発ニュースを5分でキャッチアップ)→ https://www.youtube.com/channel/UC1rXVD9WYsQPQEWZyd-A1KA/?ref=qiita