supabase-patterns
Generic Supabase best practices for Row Level Security, realtime subscriptions, storage, and edge functions. Framework-agnostic.
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o supabase-patterns.zip https://jpskill.com/download/18057.zip && unzip -o supabase-patterns.zip && rm supabase-patterns.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/18057.zip -OutFile "$d\supabase-patterns.zip"; Expand-Archive "$d\supabase-patterns.zip" -DestinationPath $d -Force; ri "$d\supabase-patterns.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
supabase-patterns.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
supabase-patternsフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
Supabase パターン Skill
あらゆるプロジェクトで Supabase を扱うための普遍的なパターンです。RLS ポリシー、リアルタイム、ストレージ、Edge Functions、およびマイグレーションを網羅します。
設計原則
この Skill は フレームワークに依存しません。普遍的な Supabase パターンを提供します。
- Book-Vetting、ocr-service、または特定のプロジェクトに特化したものではありません
- すべての Supabase プロジェクトに適用可能な共通パターンを網羅します
- プロジェクト固有の構成は、プロジェクト固有の Skill に記述します
変数
| 変数 | デフォルト | 説明 |
|---|---|---|
| SUPABASE_DIR | supabase | Supabase 構成のディレクトリ |
| ENFORCE_RLS | true | すべてのテーブルで RLS を必須にする |
| REALTIME_ENABLED | auto | リアルタイムテーブルを自動検出する |
手順
必須 - 以下のワークフローの手順を順番に実行してください。
- Supabase プロジェクトの構成を確認する
- 既存の RLS ポリシーを確認する
- セキュリティ優先のパターンに従う
- マイグレーションを整理する
危険信号 - 停止して再検討
もしあなたが以下をしようとしているなら:
- RLS ポリシーなしでテーブルを作成する
- クライアント側のコードでサービスロールキーを使用する
- スキーマの変更のためにマイグレーションをスキップする
- リアルタイムで機密データを公開する
停止 -> RLS ポリシーを追加 -> 適切なキーを使用 -> その後続行
クックブック
RLS ポリシー
- IF: RLS ポリシーを作成または変更する場合
- THEN:
./cookbook/rls-policies.mdを読んで実行する
リアルタイムサブスクリプション
- IF: リアルタイム機能を設定する場合
- THEN:
./cookbook/realtime-subscriptions.mdを読んで実行する
ストレージパターン
- IF: Supabase Storage を扱う場合
- THEN:
./cookbook/storage-patterns.mdを読んで実行する
クイックリファレンス
プロジェクト構造
supabase/
├── config.toml # プロジェクト構成
├── migrations/ # SQL マイグレーション
│ ├── 20231201000000_initial.sql
│ └── 20231202000000_add_users.sql
├── seed.sql # シードデータ
└── functions/ # Edge Functions
└── hello/
└── index.ts
主要なコマンド
# プロジェクトの初期化
supabase init
# ローカル開発の開始
supabase start
# マイグレーションの生成
supabase migration new my_migration
# リモートへのプッシュ
supabase db push
# 型の生成
supabase gen types typescript --local > types/supabase.ts
RLS ポリシーパターン
-- RLS の有効化
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- ユーザーが自分の投稿を閲覧できる
CREATE POLICY "Users can view own posts"
ON posts FOR SELECT
USING (auth.uid() = user_id);
-- ユーザーが自分の投稿を作成できる
CREATE POLICY "Users can create posts"
ON posts FOR INSERT
WITH CHECK (auth.uid() = user_id);
-- パブリック読み取り
CREATE POLICY "Public read"
ON posts FOR SELECT
USING (is_public = true);
クライアントパターン
// クライアントの初期化
import { createClient } from '@supabase/supabase-js';
import type { Database } from './types/supabase';
const supabase = createClient<Database>(
process.env.SUPABASE_URL!,
process.env.SUPABASE_ANON_KEY!
);
// 型付きクエリ
const { data, error } = await supabase
.from('posts')
.select('*')
.eq('user_id', userId);
// 挿入
const { data, error } = await supabase
.from('posts')
.insert({ title, content, user_id: userId })
.select()
.single();
リアルタイムパターン
// 変更のサブスクライブ
const subscription = supabase
.channel('posts')
.on(
'postgres_changes',
{ event: '*', schema: 'public', table: 'posts' },
(payload) => {
console.log('Change:', payload);
}
)
.subscribe();
// クリーンアップ
subscription.unsubscribe();
ストレージパターン
// ファイルのアップロード
const { data, error } = await supabase.storage
.from('avatars')
.upload(`${userId}/avatar.png`, file, {
upsert: true,
contentType: 'image/png'
});
// パブリック URL の取得
const { data: { publicUrl } } = supabase.storage
.from('avatars')
.getPublicUrl(`${userId}/avatar.png`);
セキュリティチェックリスト
本番環境前
- [ ] すべてのテーブルで RLS が有効になっている
- [ ] クライアントコードにサービスロールキーが含まれていない
- [ ] 匿名キーはパブリック操作のみに使用する
- [ ] ストレージバケットにポリシーがある
- [ ] 機密性の高い列はリアルタイムから除外されている
- [ ] API レート制限が構成されている
- [ ] CORS が適切に構成されている
RLS チェックリスト
- [ ] すべてのテーブルで RLS が有効になっている
- [ ] SELECT ポリシーが定義されている
- [ ] INSERT/UPDATE/DELETE ポリシーが定義されている
- [ ] ポリシーが異なるロールでテストされている
- [ ] 過度に寛容なポリシーがない
統合
スキーマ整合性との統合
Supabase のマイグレーションは ORM モデルと整合している必要があります。
-- supabase/migrations/20231201000000_users.sql
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT UNIQUE NOT NULL,
name TEXT,
created_at TIMESTAMPTZ DEFAULT now()
);
以下と一致する必要があります。
# SQLAlchemy モデル
class User(Base):
id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=uuid.uuid4)
email: Mapped[str] = mapped_column(unique=True)
name: Mapped[str | None]
created_at: Mapped[datetime] = mapped_column(server_default=func.now())
型生成
# ローカルスキーマから TypeScript 型を生成
supabase gen types typescript --local > types/supabase.ts
# クライアントでの使用
import type { Database } from './types/supabase';
type Post = Database['public']['Tables']['posts']['Row'];
ベストプラクティス
- RLS 優先: テーブルを作成するときは常に RLS ポリシーを追加する
- すべての変更にマイグレーション: スキーマを直接変更しない
- 型安全性: TypeScript 型を生成して使用する
- キーの衛生: クライアント側では匿名キー、サーバー側のみでサービスキーを使用する
- ポリシーのテスト: 実際のユーザーコンテキストで RLS をテストする
- リアルタイムは慎重に: 必要なテーブルのみで有効にする
📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
Supabase Patterns Skill
Universal patterns for working with Supabase in any project. Covers RLS policies, realtime, storage, edge functions, and migrations.
Design Principle
This skill is framework-generic. It provides universal Supabase patterns:
- NOT tailored to Book-Vetting, ocr-service, or any specific project
- Covers common patterns applicable across all Supabase projects
- Project-specific configurations go in project-specific skills
Variables
| Variable | Default | Description |
|---|---|---|
| SUPABASE_DIR | supabase | Directory for Supabase config |
| ENFORCE_RLS | true | Require RLS on all tables |
| REALTIME_ENABLED | auto | Auto-detect realtime tables |
Instructions
MANDATORY - Follow the Workflow steps below in order.
- Check Supabase project configuration
- Review existing RLS policies
- Follow security-first patterns
- Keep migrations organized
Red Flags - STOP and Reconsider
If you're about to:
- Create a table without RLS policies
- Use service role key in client-side code
- Skip migrations for schema changes
- Expose sensitive data in realtime
STOP -> Add RLS policies -> Use appropriate keys -> Then proceed
Cookbook
RLS Policies
- IF: Creating or modifying RLS policies
- THEN: Read and execute
./cookbook/rls-policies.md
Realtime Subscriptions
- IF: Setting up realtime features
- THEN: Read and execute
./cookbook/realtime-subscriptions.md
Storage Patterns
- IF: Working with Supabase Storage
- THEN: Read and execute
./cookbook/storage-patterns.md
Quick Reference
Project Structure
supabase/
├── config.toml # Project config
├── migrations/ # SQL migrations
│ ├── 20231201000000_initial.sql
│ └── 20231202000000_add_users.sql
├── seed.sql # Seed data
└── functions/ # Edge functions
└── hello/
└── index.ts
Key Commands
# Initialize project
supabase init
# Start local development
supabase start
# Generate migration
supabase migration new my_migration
# Push to remote
supabase db push
# Generate types
supabase gen types typescript --local > types/supabase.ts
RLS Policy Patterns
-- Enable RLS
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- User owns row
CREATE POLICY "Users can view own posts"
ON posts FOR SELECT
USING (auth.uid() = user_id);
-- User can insert own
CREATE POLICY "Users can create posts"
ON posts FOR INSERT
WITH CHECK (auth.uid() = user_id);
-- Public read
CREATE POLICY "Public read"
ON posts FOR SELECT
USING (is_public = true);
Client Patterns
// Initialize client
import { createClient } from '@supabase/supabase-js';
import type { Database } from './types/supabase';
const supabase = createClient<Database>(
process.env.SUPABASE_URL!,
process.env.SUPABASE_ANON_KEY!
);
// Query with types
const { data, error } = await supabase
.from('posts')
.select('*')
.eq('user_id', userId);
// Insert
const { data, error } = await supabase
.from('posts')
.insert({ title, content, user_id: userId })
.select()
.single();
Realtime Pattern
// Subscribe to changes
const subscription = supabase
.channel('posts')
.on(
'postgres_changes',
{ event: '*', schema: 'public', table: 'posts' },
(payload) => {
console.log('Change:', payload);
}
)
.subscribe();
// Cleanup
subscription.unsubscribe();
Storage Pattern
// Upload file
const { data, error } = await supabase.storage
.from('avatars')
.upload(`${userId}/avatar.png`, file, {
upsert: true,
contentType: 'image/png'
});
// Get public URL
const { data: { publicUrl } } = supabase.storage
.from('avatars')
.getPublicUrl(`${userId}/avatar.png`);
Security Checklist
Before Production
- [ ] RLS enabled on ALL tables
- [ ] Service role key NOT in client code
- [ ] Anon key for public operations only
- [ ] Storage buckets have policies
- [ ] Sensitive columns excluded from realtime
- [ ] API rate limiting configured
- [ ] CORS properly configured
RLS Checklist
- [ ] Every table has RLS enabled
- [ ] SELECT policies defined
- [ ] INSERT/UPDATE/DELETE policies defined
- [ ] Policies tested with different roles
- [ ] No overly permissive policies
Integration
With Schema Alignment
Supabase migrations should align with ORM models:
-- supabase/migrations/20231201000000_users.sql
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
email TEXT UNIQUE NOT NULL,
name TEXT,
created_at TIMESTAMPTZ DEFAULT now()
);
Should match:
# SQLAlchemy model
class User(Base):
id: Mapped[uuid.UUID] = mapped_column(primary_key=True, default=uuid.uuid4)
email: Mapped[str] = mapped_column(unique=True)
name: Mapped[str | None]
created_at: Mapped[datetime] = mapped_column(server_default=func.now())
Type Generation
# Generate TypeScript types from local schema
supabase gen types typescript --local > types/supabase.ts
# Use in client
import type { Database } from './types/supabase';
type Post = Database['public']['Tables']['posts']['Row'];
Best Practices
- RLS first: Always add RLS policies when creating tables
- Migrations for everything: Never modify schema directly
- Type safety: Generate and use TypeScript types
- Key hygiene: Use anon key client-side, service key server-side only
- Test policies: Test RLS with actual user contexts
- Realtime carefully: Only enable for tables that need it