要約 (TL;DR)
Supabase CLIは、Dockerを使用してPostgreSQL、Auth、Storage、Edge FunctionsといったSupabaseのフルスタックをあなたのマシン上で実行します。brew install supabase/tap/supabaseでインストールし、supabase initとsupabase startを実行してローカル環境を立ち上げ、supabase db pushとsupabase functions deployを使用して本番環境にデプロイします。これは、クラウドに触れることなくSupabaseのバックエンドを構築・テストする最速の方法です。
はじめに
開発者がローカルテストをスキップするため、バックエンドのバグの73%が本番環境で発見されます。Supabase CLIを使えば、もはやそれは言い訳になりません。5分以内に本番環境と同等の環境をマシン上で完全に実行できます。
ここに本当の問題があります。ほとんどの開発者は、直接本番環境でテストするか(危険)、クラウドと完全に一致しないローカル環境の設定に何時間も費やす(イライラする)かのどちらかです。Supabase CLIはこれら両方を解決します。本番環境を正確にミラーリングするDockerベースのローカルスタックを提供するため、ローカルで動作するものは本番環境でも動作します。
ApidogでSupabase APIをテスト - 無料
このガイドを読み終えるまでに、次のことができるようになります。
- 数分で完全なローカルSupabase環境をセットアップする
- バージョン管理されたマイグレーションでデータベーススキーマの変更を管理する
- デプロイ前にEdge Functionsをローカルで構築しテストする
- 単一のコマンドで本番環境にデプロイする
CLIなしではローカルSupabase開発が破綻する理由
SupabaseアプリをCLIなしで構築しようとしたことがあるなら、その苦痛を知っているでしょう。ここでは、常に発生する3つのシナリオを紹介します。
「本番環境でテスト」という罠。 Supabaseダッシュボードで直接スキーマ変更を行います。それは動作します。フロントエンドをプッシュします。3日後、チームメイトがリポジトリをプルすると、データベースに新しいカラムがないため、彼らのアプリは壊れます。
環境の不一致。 ローカルのPostgreSQLインスタンスをセットアップし、手動でSupabaseスキーマを再作成し、Row Level Securityポリシーがローカルで異なる動作をする理由をデバッグするのに2時間費やします。実際には異なりません。ポリシーを1つ見落としていただけです。
「私のマシンでは動く」という問題。 Edge FunctionはSupabaseダッシュボードエディタでは動作しますが、実際の環境変数ではなくハードコードされた値でテストしたため、本番環境では失敗します。
これらは特殊なケースではありません。スキーマのずれ(ローカルデータベースとリモートデータベースの同期のずれ)は、Supabaseを使用するチームにとって最も報告される問題です。CLIはこれら3つの問題をすべて解決します。
- マイグレーションにより、スキーマ変更がバージョン管理され、再現可能になります
- ローカルのDockerスタックは本番環境を正確にミラーリングし、同じPostgreSQLバージョン、同じRLSエンジンを使用します
- ローカル関数サービスは、実際の環境変数でEdge Functionsをテストします
Supabase CLIの仕組み
ローカルスタック
supabase startを実行すると、CLIは以下のサービスを含むDocker Composeスタックを起動します。
| サービス | ポート | 目的 |
|---|---|---|
| PostgreSQL | 54322 | データベース |
| PostgREST | 54321 | 自動生成されたREST API |
| GoTrue | 54321/auth | 認証サービス |
| Realtime | 54321/realtime | WebSocketサブスクリプション |
| Storage | 54321/storage | ファイルストレージ |
| Studio | 54323 | ビジュアルダッシュボード |
| Inbucket | 54324 | Eメールテスト (ローカルで全てのEメールをキャッチ) |
| Edge Runtime | 54321/functions | Denoベースの関数ランナー |
これはSupabase Cloudで実行されているのと同じスタックです。あなたのマシン上で。
インストール
macOS:
brew install supabase/tap/supabase
Windows (Scoop):
scoop bucket add supabase https://github.com/supabase/scoop-bucket.git
scoop install supabase
Linux / npm:
npm install -g supabase
動作確認:
supabase --version
# supabase 1.x.x
supabase startプロジェクトのセットアップ
mkdir my-project && cd my-project
supabase init
これにより、以下が作成されます。
supabase/
├── config.toml # ポート、認証設定、ストレージ設定
├── seed.sql # データベースリセットのたびにロードされる開発データ
└── migrations/ # スキーマのバージョン履歴
ローカルスタックの起動
supabase start
初回実行時には約1GBのDockerイメージがダウンロードされます。それ以降は、起動に約10秒かかります。
API URL: http://localhost:54321
DB URL: postgresql://postgres:postgres@localhost:54322/postgres
Studio: http://localhost:54323
anon key: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
anon keyを.env.localファイルにコピーしてください。これはフロントエンドに必要です。
マイグレーションによるデータベース管理
マイグレーションはCLIワークフローの核です。すべてのスキーマ変更は、Gitで追跡されるバージョン管理されたSQLファイルになります。「誰がいつデータベースを変更したのか」という問題はもうありません。
最初のマイグレーションの作成
supabase migration new create_posts_table
# 作成されるファイル: supabase/migrations/20260324120000_create_posts_table.sql
ファイルを編集:
-- RLS付きのpostsテーブルを作成
CREATE TABLE posts (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL,
title TEXT NOT NULL,
content TEXT,
published BOOLEAN DEFAULT false,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- 行レベルセキュリティを有効にする
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- 誰でも公開された投稿を読める
CREATE POLICY "Anyone can read published posts"
ON posts FOR SELECT
USING (published = true);
-- ユーザーは自分の投稿を管理する
CREATE POLICY "Users manage own posts"
ON posts FOR ALL
USING (auth.uid() = user_id);
-- 変更ごとにupdated_atを自動更新
CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER posts_updated_at
BEFORE UPDATE ON posts
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
適用:
supabase migration up
TypeScript型の生成
スキーマ変更ごとに、型を再生成します。
supabase gen types typescript --local > src/types/database.ts
フロントエンドは完全な型安全性を得られます。
import { Database } from '@/types/database'
type Post = Database['public']['Tables']['posts']['Row']
type NewPost = Database['public']['Tables']['posts']['Insert']
// これで、ランタイム前にエディターが型エラーをキャッチします
const createPost = async (post: NewPost) => {
const { data, error } = await supabase
.from('posts')
.insert(post)
.select()
.single()
return data
}
開発データのシーディング
supabase/seed.sqlを編集:
-- テストユーザー (ローカル開発のために認証をバイパス)
INSERT INTO auth.users (id, email) VALUES
('00000000-0000-0000-0000-000000000001', 'alice@example.com'),
('00000000-0000-0000-0000-000000000002', 'bob@example.com');
-- テスト投稿
INSERT INTO posts (user_id, title, content, published) VALUES
('00000000-0000-0000-0000-000000000001', 'Supabase入門', '学んだことを紹介...', true),
('00000000-0000-0000-0000-000000000002', '下書き: API設計パターン', '作業中...', false);
いつでもリセットして再シーディング:
supabase db reset
これにより、すべてが削除され、すべてのマイグレーションが再実行され、シードデータがロードされます。毎日実行して、クリーンな状態から開始しましょう。
ApidogでSupabase APIをテストする
ローカルのSupabaseが起動すると、http://localhost:54321で完全に機能するREST APIが利用可能になります。Supabaseは、PostgRESTを介してすべてのテーブルのエンドポイントを自動生成します。これらをcurlで手動でテストするのは、特に異なるユーザートークンでRLSポリシーをテストする必要がある場合、すぐに面倒になります。
ApidogはローカルのSupabaseインスタンスに直接接続します。次のことができます。
- リクエストを再利用可能なコレクションとして保存する
- 環境を切り替えることで、異なるユーザーとして同じエンドポイントをテストする
- アサーション(「応答に少なくとも1つの投稿が含まれている」など)を追加し、テストスイートとして実行する
- APIドキュメントをチームと自動的に共有する
ApidogをローカルSupabaseで設定する:
- Apidogで新しいプロジェクトを作成
- ベースURLを設定:
http://localhost:54321 - 環境変数を追加:
anon_key = your-local-anon-key - 認証ヘッダーを追加:
Bearer {{anon_key}}
投稿エンドポイントのテスト:
GET http://localhost:54321/rest/v1/posts?published=eq.true
Authorization: Bearer {{anon_key}}
apikey: {{anon_key}}
これをリクエストとして保存し、応答に少なくとも1つの投稿が含まれているというアサーションを追加し、RLSポリシーを変更するたびに実行してください。これにより、本番環境に到達する前に、壊れたポリシーを検出できます。
ApidogでSupabase APIのテストを開始 - 無料
Edge Functions: ローカルでの構築とテスト
Edge FunctionsはDeno上で、ユーザーに近いエッジで実行されます。Webhooks、バックグラウンドジョブ、サーバーサイドロジックが必要なAPIエンドポイントに最適です。
関数の作成
supabase functions new send-welcome-email
これにより、supabase/functions/send-welcome-email/index.tsが作成されます。
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts'
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2'
serve(async (req) => {
const { user_id } = await req.json()
// サービスロールはRLSをバイパスします - 慎重に使用してください
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
)
const { data: profile } = await supabase
.from('profiles')
.select('email, full_name')
.eq('id', user_id)
.single()
// ここにEメール送信ロジックを記述します
console.log(`Sending welcome email to ${profile?.email}`)
return new Response(
JSON.stringify({ success: true }),
{ headers: { 'Content-Type': 'application/json' } }
)
})
ホットリロードでローカルテスト
supabase functions serve
関数サーバーはファイルの変更を監視し、自動的にリロードします。テストするには:
curl -X POST http://localhost:54321/functions/v1/send-welcome-email \
-H "Authorization: Bearer YOUR_ANON_KEY" \
-H "Content-Type: application/json" \
-d '{"user_id": "00000000-0000-0000-0000-000000000001"}'
本番環境へのデプロイ
# 一つの関数をデプロイ
supabase functions deploy send-welcome-email
# 全ての関数をデプロイ
supabase functions deploy
高度なテクニックと実績のあるアプローチ
シークレット管理
APIキーを関数にハードコードしないでください。シークレットを使用します。
# 本番環境のシークレットを設定
supabase secrets set RESEND_API_KEY=re_xxx STRIPE_KEY=sk_live_xxx
# 全てのシークレットを一覧表示
supabase secrets list
# シークレットを削除
supabase secrets unset STRIPE_KEY
関数内でそれらにアクセスします。
const resendKey = Deno.env.get('RESEND_API_KEY')
// 決してこのようにしてはいけません: const resendKey = 're_xxx'
データベースのブランチング
大規模なスキーマ変更に取り組んでいますか?分離されたブランチを作成してください。
supabase branches create feature-payments
supabase branches switch feature-payments
# 変更を加え、テストし、マージする
supabase branches merge feature-payments
これにより、実験中もメインの開発データベースをクリーンに保つことができます。
避けるべきよくある間違い
Studioでデータベースを直接編集する。 常にマイグレーションを使用してください。直接編集すると追跡されず、チームメイトはそれらの変更を把握できません。
.envファイルをコミットする。 本番環境にはsupabase secrets setを使用してください。.env*を.gitignoreに追加してください。
プル後にsupabase db resetをスキップする。 チームメイトの変更をプルした場合、新しいマイグレーションをローカルで実行する必要があります。それらを適用するためにリセットしてください。
スキーマ変更後に型を再生成しない。 カラムを追加した瞬間にTypeScriptの型は古くなります。型生成をマイグレーションワークフローの一部にしてください。
ローカルテストなしで関数をデプロイする。 デプロイする前に、常にsupabase functions serveを実行し、実際のリクエストでテストしてください。
フロントエンドコードでサービスロールキーを使用する。 サービスロールキーはRLSをバイパスします。Edge Functionsとサーバーサイドコードのみに属し、ブラウザには決して使用しないでください。
パフォーマンスのヒント
# メモリを節約するために不要なサービスをスキップ
supabase start --exclude-studio --exclude-inbucket
# リソースを消費しているものを確認
docker stats
代替手段と比較
| 機能 | Supabase CLI | Firebase CLI | PlanetScale CLI |
|---|---|---|---|
| ローカルデータベース | 完全なPostgreSQL | エミュレーターのみ | クラウドのみ |
| マイグレーション | Git内のSQLファイル | ネイティブサポートなし | ブランチング |
| Edge Functions | Denoランタイム | Cloud Functions | 含まれていない |
| ローカル認証 | 完全なGoTrue | エミュレーター | 含まれていない |
| オープンソース | 完全にオープン | プロプライエタリ | プロプライエタリ |
| 型生成 | 組み込み | 手動 | 手動 |
Firebaseのローカルエミュレーターは素早いプロトタイピングには良いですが、実際のPostgreSQLインスタンスは提供しません。PlanetScaleのブランチングモデルはスキーマ変更に優れていますが、常にクラウドに対して作業することになります。Supabase CLIは、完全にオープンソースでPostgreSQLネイティブのローカル開発体験を望むチームにとって優れています。
実際のユースケース
マルチテナントデータを持つSaaSアプリケーション。 あるフィンテックスタートアップは、3つの環境(開発、ステージング、本番)で47のマイグレーションを管理しています。RLSポリシーは、コードが本番環境に到達する前に、異なるユーザーロールでローカルでテストされます。結果:6ヶ月間でスキーマ関連の本番インシデントはゼロでした。
Eコマースの注文処理。 あるEコマースチームは、Stripeのウェブフック処理にEdge Functionsを使用しています。彼らはsupabase functions serveを使って、実際のStripeテストイベントでウェブフックのペイロードをローカルでテストしています。デプロイ時間は2時間から15分に短縮されました。
モバイルアプリのバックエンド。 あるReact Nativeチームは、マイグレーションごとにTypeScriptの型を生成し、それを社内のnpmパッケージとして共有しています。フロントエンドとバックエンドは自動的に同期されます。「このエンドポイントはどのようなフィールドを返すのか?」といったSlackでの質問はもうありません。
まとめ
これで次のことができるようになりました:
- 数分で完全なローカルSupabase環境をセットアップする
- マイグレーションを使用して、すべてのスキーマ変更をバージョン管理する
- ホットリロードでEdge Functionsをローカルでテストする
- スキーマからTypeScriptの型を自動生成する
supabase db pushとsupabase functions deployでデプロイする- ApidogでAPIを本番環境にリリースする前にテストする
このワークフローはすぐに効果を発揮します。チームはより速く出荷し、バグを早期に発見し、二度とスキーマのずれに悩まされることはありません。
次のステップ:
- インストール:
brew install supabase/tap/supabase - プロジェクトで
supabase initを実行 - 最初のマイグレーションを作成
- Apidogを設定してローカルエンドポイントをテスト
- 自信を持って本番環境にデプロイ
ApidogでSupabase APIをテスト - 無料
よくある質問
Supabase CLIを使用するにはDockerが必要ですか?はい。supabase startを実行する前にDocker Desktopが実行されている必要があります。CLIはDocker Composeを使用して、完全なスタックをローカルで実行します。Dockerが実行されていない場合、「Cannot connect to Docker daemon」エラーが発生します。
ローカルデータベースを本番環境と同期するにはどうすればよいですか?supabase db pullを使用してリモートスキーマからマイグレーションを生成し、supabase db pushを使用してローカルのマイグレーションを本番環境に適用します。プル後にローカルでsupabase db resetを実行して、環境が一致していることを確認してください。
Supabase CLIをSupabase Cloudアカウントなしで使用できますか?はい。CLIは、クラウドアカウントなしで開発のために完全にローカルで使用できます。本番環境にデプロイする準備ができたときにのみ、supabase loginとsupabase linkが必要です。
チームでマイグレーションの競合を処理するにはどうすればよいですか?新しいマイグレーションを作成する前に、最新のGit変更をプルし、supabase db resetを実行してください。記述的なマイグレーション名を使用し、破壊的なスキーマ変更を行う際にはチームとコミュニケーションを取ってください。
supabase db pushとsupabase migration upの違いは何ですか?supabase migration upは、保留中のマイグレーションをローカルデータベースに適用します。supabase db pushは、それらをリモート(本番)プロジェクトに適用します。常に最初にローカルでテストしてください。
既存のプロジェクトでSupabase CLIを使用できますか?はい。supabase link --project-ref YOUR_PROJECT_IDを実行して既存のプロジェクトにリンクし、supabase db pullを実行して現在のリモートスキーマからマイグレーションを生成できます。
RLSポリシーをローカルでテストするにはどうすればよいですか?http://localhost:54323のSupabase Studioを使用してユーザーロールを切り替えるか、異なるJWTトークンを使用してAPI経由でテストします。Apidogを使用すると、複数の環境を異なるユーザートークンで作成し、同じリクエストを異なるユーザーとして実行することで、これが簡単になります。
Supabase CLIは無料ですか?はい。CLIは無料でオープンソースです。ローカル開発には費用はかかりません。Supabase Cloudリソースは、本番環境にデプロイした場合にのみ料金が発生します。
