Supabase
BaaSプラットフォーム
Supabase
概要
Supabaseは、オープンソースのFirebase代替として位置づけられている、次世代のBaaS(Backend as a Service)プラットフォームです。PostgreSQLをベースとしたリレーショナルデータベース、リアルタイム機能、認証、ストレージ、Edge Functionsなど、現代的なWebアプリケーション開発に必要なバックエンド機能をすべて提供します。
2020年に設立され、オープンソース志向の開発者コミュニティに急速に普及しており、特にPostgreSQLの堅牢性とオープンソースアプローチを求める開発者に支持されています。SQL-ファーストのアプローチとPostgreSQLの強力な機能を最大限活用できる設計が特徴的です。
詳細
核となる技術要素
PostgreSQL中心のアーキテクチャ
- 完全管理されたPostgreSQL 15+を採用
- Row Level Security(RLS)による細かな権限制御
- 豊富なPostgreSQL拡張機能に対応
- SQLクエリの直接実行とPostgREST APIの自動生成
リアルタイム機能
- WebSocketベースのリアルタイムデータ同期
- Broadcast:チャンネル間でのメッセージ配信
- Presence:ユーザーのオンライン状態管理
- Postgres Changes:データベース変更の自動通知
認証システム
- JWT(JSON Web Token)ベースの認証
- 30+のOAuthプロバイダー対応(Google、GitHub、Apple等)
- Magic Link、SMS OTP、メール認証
- Multi-Factor Authentication(MFA)サポート
ストレージ機能
- S3互換のオブジェクトストレージ
- 画像変換とリサイズの自動処理
- CDN統合による高速配信
- きめ細かなアクセス制御
Edge Functions
- Deno 2.1ランタイム対応
- グローバルエッジデプロイ
- TypeScript/JavaScriptサポート
- 2025年新機能:WebSocket、バックグラウンドタスク、エフェメラルストレージ
技術的特徴
開発者体験
- ダッシュボードからのコード編集・デプロイ
- AI Assistant統合(SQL生成、パフォーマンス分析)
- TypeScript型定義の自動生成
- 複数言語のSDK(JavaScript、Python、Dart、Swift、Kotlin等)
パフォーマンス最適化
- 地理的ルーティング(2025年4月開始)
- Dedicated Pooler(PgBouncer)による接続管理
- 読み込みレプリカ対応
- クエリ実行計画の可視化
エンタープライズ対応
- SOC 2 Type II認証取得
- プロジェクトスコープドロール
- 組織レベルの権限管理
- 99.9%のSLA
メリット・デメリット
メリット
SQL-ファーストのアプローチ
- PostgreSQLの全機能を活用可能
- 複雑なクエリとJOINに対応
- インデックス、ビュー、トリガーなどの高度な機能
- 既存のSQL知識を直接活用
オープンソースと透明性
- Apache 2.0ライセンス
- セルフホスティング可能
- ベンダーロックインの回避
- コミュニティドリブンの開発
豊富なリアルタイム機能
- データベース変更の自動検知
- 低遅延でのデータ同期
- ブロードキャストとプレゼンス機能
- WebSocketサポート
開発効率の向上
- 型安全なAPI自動生成
- AI Assistant支援
- ダッシュボードでの直接開発
- 豊富なテンプレートと例
コスト効率
- 無料プランでも十分な機能
- 使用量ベースの柔軟な課金
- オープンソースによる選択肢
デメリット
学習曲線
- PostgreSQLとSQLの知識が必要
- Row Level Security(RLS)の設計複雑性
- リアルタイム機能の設定が初心者には難しい
制約と限界
- Firebaseほどの成熟度はまだない
- NoSQLライクな柔軟性は限定的
- 一部機能がベータ版段階
運用面での考慮事項
- セルフホスティング時の運用負荷
- 大規模トラフィック時のチューニング必要性
- バックアップとディザスタリカバリの計画要
参考ページ
- Supabase公式サイト
- Supabase Documentation
- Supabase GitHub リポジトリ
- Supabase Examples
- Supabase Blog
- Supabase Discord Community
書き方の例
1. 基本セットアップとプロジェクト設定
# Supabase CLIのインストール
npm install -g @supabase/cli
# 新しいプロジェクトの初期化
supabase init my-project
cd my-project
# ローカル開発環境の起動
supabase start
# 本番プロジェクトとのリンク
supabase link --project-ref your-project-id
// TypeScript/JavaScript クライアントの初期化
import { createClient } from '@supabase/supabase-js'
const supabaseUrl = 'https://your-project.supabase.co'
const supabaseKey = 'your-anon-key'
const supabase = createClient(supabaseUrl, supabaseKey)
// 型安全なクライアント設定
export interface Database {
public: {
Tables: {
profiles: {
Row: {
id: string
username: string | null
avatar_url: string | null
website: string | null
updated_at: string | null
}
Insert: {
id: string
username?: string | null
avatar_url?: string | null
website?: string | null
updated_at?: string | null
}
Update: {
id?: string
username?: string | null
avatar_url?: string | null
website?: string | null
updated_at?: string | null
}
}
}
}
}
const typedSupabase = createClient<Database>(supabaseUrl, supabaseKey)
2. データベース操作(PostgreSQL)
-- テーブルの作成とRow Level Security設定
CREATE TABLE public.profiles (
id UUID REFERENCES auth.users NOT NULL,
updated_at TIMESTAMP WITH TIME ZONE,
username TEXT UNIQUE,
avatar_url TEXT,
website TEXT,
full_name TEXT,
PRIMARY KEY (id),
CONSTRAINT username_length CHECK (char_length(username) >= 3)
);
-- Row Level Security有効化
ALTER TABLE public.profiles ENABLE ROW LEVEL SECURITY;
-- セキュリティポリシーの作成
CREATE POLICY "Public profiles are viewable by everyone."
ON profiles FOR SELECT
USING (true);
CREATE POLICY "Users can insert their own profile."
ON profiles FOR INSERT
WITH CHECK ((SELECT auth.uid()) = id);
CREATE POLICY "Users can update own profile."
ON profiles FOR UPDATE
USING ((SELECT auth.uid()) = id);
// TypeScript データベースクエリ
import { supabase } from './supabase'
// レコードの作成
const { data, error } = await supabase
.from('profiles')
.insert([
{
id: user.id,
username: 'example_user',
full_name: 'Example User',
avatar_url: '/default-avatar.png'
}
])
.select()
// 複雑なクエリ(JOIN、フィルター、ソート)
const { data: postsWithAuthors } = await supabase
.from('posts')
.select(`
id,
title,
content,
created_at,
profiles!inner (
username,
avatar_url
)
`)
.eq('status', 'published')
.order('created_at', { ascending: false })
.limit(10)
// リアルタイムクエリ
const { data: todos } = await supabase
.from('todos')
.select('*')
.eq('user_id', user.id)
.order('created_at', { ascending: false })
3. 認証とユーザー管理
// メール/パスワード認証
const signUp = async (email: string, password: string) => {
const { data, error } = await supabase.auth.signUp({
email,
password,
options: {
emailRedirectTo: `${window.location.origin}/auth/callback`
}
})
return { data, error }
}
const signIn = async (email: string, password: string) => {
const { data, error } = await supabase.auth.signInWithPassword({
email,
password
})
return { data, error }
}
// OAuth認証(Google例)
const signInWithGoogle = async () => {
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'google',
options: {
redirectTo: `${window.location.origin}/auth/callback`
}
})
return { data, error }
}
// Magic Link認証
const sendMagicLink = async (email: string) => {
const { data, error } = await supabase.auth.signInWithOtp({
email,
options: {
emailRedirectTo: `${window.location.origin}/auth/callback`
}
})
return { data, error }
}
// セッション管理
const getSession = async () => {
const { data: { session } } = await supabase.auth.getSession()
return session
}
const getUser = async () => {
const { data: { user } } = await supabase.auth.getUser()
return user
}
// 認証状態の監視
supabase.auth.onAuthStateChange((event, session) => {
if (event === 'SIGNED_IN') {
console.log('User signed in:', session?.user)
} else if (event === 'SIGNED_OUT') {
console.log('User signed out')
}
})
4. リアルタイムサブスクリプション
// データベース変更の監視
const subscribeToProfile = (userId: string) => {
return supabase
.channel('profile-changes')
.on(
'postgres_changes',
{
event: '*',
schema: 'public',
table: 'profiles',
filter: `id=eq.${userId}`
},
(payload) => {
console.log('Profile changed:', payload)
}
)
.subscribe()
}
// ブロードキャスト機能
const channel = supabase.channel('room-1')
// ブロードキャストの送信
const sendMessage = (message: string) => {
channel.send({
type: 'broadcast',
event: 'message',
payload: { message, user: user.id }
})
}
// ブロードキャストの受信
channel
.on('broadcast', { event: 'message' }, (payload) => {
console.log('Received message:', payload)
})
.subscribe()
// プレゼンス機能(オンライン状態管理)
const trackPresence = async () => {
const presenceTrackStatus = await channel.track({
user: user.id,
online_at: new Date().toISOString(),
})
}
channel
.on('presence', { event: 'sync' }, () => {
const newState = channel.presenceState()
console.log('Online users:', newState)
})
.on('presence', { event: 'join' }, ({ key, newPresences }) => {
console.log('User joined:', key, newPresences)
})
.on('presence', { event: 'leave' }, ({ key, leftPresences }) => {
console.log('User left:', key, leftPresences)
})
.subscribe()
5. ファイルストレージとEdge Functions
// ファイルアップロード
const uploadFile = async (file: File, path: string) => {
const { data, error } = await supabase.storage
.from('avatars')
.upload(path, file, {
cacheControl: '3600',
upsert: false
})
return { data, error }
}
// ファイルのダウンロード
const downloadFile = async (path: string) => {
const { data, error } = await supabase.storage
.from('avatars')
.download(path)
return { data, error }
}
// 公開URLの取得
const getPublicUrl = (path: string) => {
const { data } = supabase.storage
.from('avatars')
.getPublicUrl(path)
return data.publicUrl
}
// 署名付きURLの作成
const createSignedUrl = async (path: string, expiresIn: number = 3600) => {
const { data, error } = await supabase.storage
.from('private-files')
.createSignedUrl(path, expiresIn)
return { data, error }
}
// Edge Function の呼び出し
const callEdgeFunction = async (functionName: string, payload: any) => {
const { data, error } = await supabase.functions.invoke(functionName, {
body: payload,
headers: {
'Content-Type': 'application/json',
}
})
return { data, error }
}
6. 本番デプロイとマネジメント
# データベーススキーマの適用
supabase db push
# Edgeファンクションのデプロイ
supabase functions deploy hello-world
# シークレット環境変数の設定
supabase secrets set API_KEY=your-secret-key
# データベースマイグレーション
supabase db diff --file migration_name
supabase db reset
# 型定義の生成
supabase gen types typescript --project-id your-project-id > types/database.types.ts
// 本番環境向け設定
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
const supabase = createClient(supabaseUrl, supabaseAnonKey, {
auth: {
autoRefreshToken: true,
persistSession: true,
detectSessionInUrl: true
},
realtime: {
params: {
eventsPerSecond: 10,
},
},
})
// パフォーマンス監視
const monitorQuery = async () => {
const start = Date.now()
const { data, error } = await supabase
.from('posts')
.select('*')
.limit(100)
const duration = Date.now() - start
console.log(`Query executed in ${duration}ms`)
if (error) {
console.error('Query error:', error)
}
return { data, error, duration }
}
// エラーハンドリング
const handleSupabaseError = (error: any) => {
switch (error.code) {
case 'PGRST301':
return 'Resource not found'
case '23505':
return 'Duplicate entry'
case '42501':
return 'Insufficient privileges'
default:
return error.message || 'An unknown error occurred'
}
}