🔐

Supabaseでレコードが取得できなかった件(RLSを学ぶ)

に公開

Supabaseでレコードが取得できなかった件(RLSを学ぶ)

はじめに

Supabaseを試しに使ってみたのですが、データベースからレコードが取得できないという問題に当たりました。
この記事では、私がこの問題を解決する中で学んだ**Row Level Security (RLS)**について、解説します。


問題の背景

SupabaseのAPIを使って、以下のように簡単なクエリを実行したところ、データが取得できませんでした。

const { data, error } = await supabase.from('Users').select('*');
console.log(data); // => []

原因

結論から言うと、デフォルトで**Row Level Security (RLS)**が有効になっており、このため、適切なポリシーを設定していなかったため、データが取得できていませんでした。

RLS(行レベルセキュリティ) とは

Supabaseでは、データベースのセキュリティを強化するためにRLS(行レベルセキュリティ)がデフォルトで有効になっています。

RLSの基本的な仕組み

RLS: 行単位でデータへのアクセス制御を設定する機能。
ポリシー: 「誰がどのデータにアクセスできるか」を定義するルール。

例えば、以下のポリシーが設定されている場合、現在ログイン中のユーザーのデータしか取得できません。

CREATE POLICY "View own data only"
ON Users
FOR SELECT
USING (user_id = auth.uid());

参考文献

https://supabase.com/docs/guides/database/postgres/row-level-security

どういうメカニズム?

設定したポリシーがテーブルごとに関連付けられており、ユーザーがテーブルにアクセスするたびにポリシーが実行されます。
具体的には、クエリに対して、WHERE句が追加されるようなイメージです。
例として、上記で挙げていたポリシーを設定していた場合、

クライアントが送信するSQLクエリ:

SELECT * FROM Users;

データベースでの実際のSQLクエリ

SELECT * FROM Users WHERE user_id = auth.uid();

上記のように変化します。

で、結局どうすればいいの

解決策1: 認証ユーザーとしてSupabaseに接続

const { data: user, error } = await supabase.auth.signInWithPassword({
    email: 'user@example.com', // DB内に登録したメールアドレス
    password: 'your-password', // DB内に登録したパスワード
});

if (error) {
    console.error('Authentication Error:', error);
} else {
    console.log('Authenticated User:', user);
}

クライアントからsqlを叩く前に上記のような処理を挟んで認証を成功させる必要があります。

解決策2: ポリシーを緩くする(開発中のみ、本番では禁止)

Supabase 側でポリシーを緩和させれば簡単に閲覧可能になります。
TableEdiorから任意のテーブル選択後、Auth policyを選択し、そこでポリシーを追加すればいけます。

参考記事

https://zenn.dev/yoshikawa_fuma/articles/5317fd9bcfdfa8

まとめと学んだこと

  • RLSの基本を理解:
    • Supabaseでは、RLSがデフォルトで有効されていますし、有効にした方が堅牢なアプリケーションが作成できる。
    • データ取得にはポリシーに合った条件が必要となる。
  • 認証を最初に実装する:
    • auth.uid()を利用するポリシーでは、認証が必須。
    • supabaseにはauth.uid()のような便利なヘルパーがある。

Discussion