Supabase データベース設計:テーブル構造・リレーション・Row Level Security 完全ガイド
Supabase Dashboard に表示された赤い警告マーク――「RLS not enabled」をじっと見つめる。頭の中にいくつもの疑問が浮かぶ。ユーザーの記事データは漏れないだろうか? この外部キーのリレーションは正しいのか? 多対多のリレーションは結局どう作ればいいのか?
Supabase を使い始めたころは、本当にたくさんの落とし穴にはまりました。テーブルを作ったのに RLS の有効化を忘れ、誰でも全データを読み取れる状態になっていたこと。外部キーの設計がずさんで、ユーザーデータを削除しても記事が残ってしまったこと。多対多の中間テーブルを、なんと配列で代用しようとして――完全な大惨事になったこと。
数か月もがいて、ようやく Supabase のデータベース設計の勘どころがつかめてきました。この記事では、その経験をまとめて紹介します。
一、テーブル構造の設計:PostgreSQL の命名規則
1.1 命名規則:snake_case こそ王道
PostgreSQL には少し変わった癖があります。引用符で囲まないと識別子をすべて小文字に変換し、ダブルクォートで囲むと書いたとおりに厳密に扱うのです。
これは何を意味するのでしょうか。キャメルケース(UserProfile)で命名すると、あらゆる箇所でダブルクォートを付ける必要があります。とても面倒です。
そのため PostgreSQL コミュニティの慣例はこうです。snake_case(アンダースコア区切り)、テーブル名は複数形、カラム名は単数形。
-- ✅ 推奨
CREATE TABLE users (
id UUID PRIMARY KEY,
email TEXT UNIQUE,
created_at TIMESTAMPTZ
);
1.2 カラム型の選び方:MySQL の発想に縛られないこと
間違い 1:TEXT ではなく VARCHAR を使う
PostgreSQL では、TEXT と VARCHAR のパフォーマンスは完全に同じです。違いは VARCHAR(n) に長さ制限があるだけ。本当に長さを制限したい場合を除けば、そのまま TEXT を使いましょう。
間違い 2:TIMESTAMPTZ ではなく TIMESTAMP を使う
TIMESTAMP はタイムゾーン情報を保存しません。サーバーがアメリカ、ユーザーが中国にいると、表示時刻がめちゃくちゃになります。TIMESTAMPTZ なら自動でタイムゾーンを変換してくれます。
間違い 3:UUID ではなく SERIAL を使う
SERIAL は自動採番の整数です。単一マシンのアプリなら問題ありませんが、分散システムでは衝突します。UUID はグローバルに一意です。
二、3 種類のテーブルリレーション:1 対 1・1 対多・多対多
2.1 1 対 1:UNIQUE を付けるだけ
最もよくある場面は、ユーザーとプロフィールです。
CREATE TABLE profiles (
id UUID PRIMARY KEY,
user_id UUID UNIQUE REFERENCES users(id) ON DELETE CASCADE,
bio TEXT
);
ポイントは user_id UUID UNIQUE です。UNIQUE 制約によって、各ユーザーが持てるプロフィールは 1 つだけになります。
2.2 1 対多:もっとも普通の外部キー
著者と書籍です。1 人の著者は何冊もの本を書けます。
CREATE TABLE books (
id UUID PRIMARY KEY,
author_id UUID REFERENCES authors(id) ON DELETE CASCADE,
title TEXT
);
クエリのときは、Supabase JS で関連データを直接ネストして取得できます。
2.3 多対多:中間テーブルが鍵
学生と科目です。1 人の学生は複数の科目を履修でき、1 つの科目には複数の学生が登録できます。
解決策は、中間テーブルを作ることです。
CREATE TABLE enrollments (
student_id UUID REFERENCES students(id) ON DELETE CASCADE,
course_id UUID REFERENCES courses(id) ON DELETE CASCADE,
PRIMARY KEY (student_id, course_id)
);
三、Row Level Security:データベース自身が用心棒になる
3.1 RLS の「デフォルト拒否」という思想
私が初めて Supabase を使ったとき、posts テーブルを作り、anon key を使ってフロントエンドから直接クエリしました。その結果――すべてのデータが返ってきたのです。ぎょっとしました。
実は Supabase はデフォルトで Row Level Security(RLS)が無効になっています。有効化していないと、anon key を手に入れた人なら誰でも全データを読み書きできてしまいます。
そこで第一の鉄則です。テーブルを作ったら、すぐに RLS を有効化する。
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
RLS を有効化したら終わりかというと、まだです。有効化してもポリシーがなければ「すべてのアクセスを拒否」と同じ意味になります。少なくとも 1 つのポリシーを作る必要があります。
3.2 ポリシー構文:USING と WITH CHECK
- USING:既存の行をフィルタする(SELECT・UPDATE・DELETE)
- WITH CHECK:新しい行を検証する(INSERT・UPDATE)
3.3 よくある 4 つのポリシーパターン
パターン 1:ユーザーが自分のデータにアクセスする
CREATE POLICY "Users manage own data"
ON posts FOR ALL
TO authenticated
USING (user_id = auth.uid());
パターン 2:公開データと非公開データの混在
公開済みのものは全員に見え、下書きは作者だけに見えます。
パターン 3:マルチテナントの分離
チームメンバーは自分のチームのデータにしかアクセスできません。
パターン 4:RBAC によるロール制御
管理者には特別な権限があります。
四、RLS のパフォーマンス最適化
4.1 パフォーマンスの大敵:サブクエリが行ごとに 1 回ずつ実行される
RLS ポリシー内のサブクエリは、データの 1 行ごとに 1 回実行されます。10 万行のデータに対して、ポリシーにチームのリレーションを確認するサブクエリが入っていると――クエリが 3 分でタイムアウトしました。
4.2 最適化案その 1:インデックスを追加する
RLS ポリシーで使うカラムには、必ずインデックスを付けます。
CREATE INDEX idx_posts_user_id ON posts(user_id);
Supabase 公式のテストでは、インデックスなしで 450ms、ありで 45ms。10 倍の向上です。
4.3 最適化案その 2:SECURITY DEFINER 関数
サブクエリを関数にまとめ、1 回だけ実行させます。
CREATE OR REPLACE FUNCTION user_teams()
RETURNS SETOF UUID
LANGUAGE SQL SECURITY DEFINER STABLE
AS $$ SELECT team_id FROM team_members WHERE user_id = auth.uid(); $$;
五、実践例
5.1 ブログシステム:記事・カテゴリ・タグ
完全な実装には、テーブル構造、RLS ポリシー、インデックス設定が含まれます。
5.2 マルチテナント SaaS:チームコラボレーション
チームデータの分離、チームメンバーのアクセス、管理者権限の制御を扱います。
まとめ
核心となるポイント:
- snake_case で命名する
- 主キーは UUID、文字列は TEXT、時刻は TIMESTAMPTZ
- RLS は必ず有効化する
- インデックス + SECURITY DEFINER 関数で最適化する
FAQ
テーブル作成後はすぐに RLS を有効化しないといけませんか?
なぜ PostgreSQL では snake_case が推奨されるのですか?
RLS ポリシーで FOR ALL を使ってはいけないのはなぜですか?
RLS の状態はどうやって確認しますか?
2分で読めます · 公開日: 2026年4月4日 · 更新日: 2026年6月8日
Supabase 実践ガイド
検索からこのページに来た場合は、前後の記事もあわせて読むと同じテーマの理解がかなり早く深まります。
前の記事
Supabase 入門:PostgreSQL + Auth + Storage のオールインワンバックエンド
Supabase はオープンソースの Firebase 代替で、PostgreSQL データベース、企業レベルの認証、オブジェクトストレージ、リアルタイム同期を提供します。本チュートリアルは3つのコア機能を素早く使い始める方法を、完全なコード例と実践ケースとともに解説。フロントエンド開発者がフルスタックアプリを手早く構築するのに最適です
第 1 / 10 記事
次の記事
Supabase Auth 実践:メール認証・OAuth・セッション管理
Supabase Auth 実践ガイド。メール認証の設定、OAuth 連携、JWT セッション管理、PKCE フローまで。ユーザー認証をまとめて解決します。
第 3 / 10 記事
関連記事
Supabase Storage 実践:ファイルアップロード・権限制御・CDN 高速化
Supabase Storage 実践:ファイルアップロード・権限制御・CDN 高速化
Supabase Realtime 実践:3つのモード比較とコラボアプリ開発
Supabase Realtime 実践:3つのモード比較とコラボアプリ開発
Supabase Realtime 実践:WebSocket 接続管理と切断再接続戦略
コメント
GitHubアカウントでログインしてコメントできます