セットアップ
プロジェクト作成、API キーの種類、ローカル開発環境、CLI、最小サンプルまで。
1. プロジェクト作成(クラウド)
- supabase.com/dashboard でアカウント作成
- 「New Project」→ 組織を選んで名前・パスワード・リージョンを入力
- リージョンはユーザに近い場所(日本なら ap-northeast-1 / Tokyo)
- プロビジョニングが完了するまで 1〜2 分
2. API キーの種類(最重要)
プロジェクト Settings → API で 3 種類のキーが見られる。役割を絶対に間違えない。
| キー | 用途 | 場所 |
|---|---|---|
| Project URL | API のベース URL | クライアント / サーバ両方 OK |
| anon key | RLS 前提の公開可キー | クライアント(ブラウザ)に置いてよい |
| service_role key | RLS をバイパスする管理者キー | サーバ専用、絶対漏らさない |
事故が起きる典型
service_role key をブラウザに埋め込んでしまう → 全データが読み書き可能に。 フロントは常に anon key。サーバ(Route Handler / Edge Function)でだけ service_role を使う。
3. supabase-js のインストール
npm install @supabase/supabase-js
4. クライアントの初期化
// lib/supabase.ts
import { createClient } from "@supabase/supabase-js"
export const supabase = createClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
)
環境変数
# .env.local
NEXT_PUBLIC_SUPABASE_URL=https://xxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOi...
# サーバ専用
SUPABASE_SERVICE_ROLE_KEY=eyJhbGciOi...
5. 最小サンプル(テーブル作成 → 取得)
SQL Editor でテーブル作成
create table posts (
id uuid primary key default gen_random_uuid(),
title text not null,
content text,
created_at timestamptz default now()
);
-- まず RLS を有効化(最初に必ず)
alter table posts enable row level security;
-- 全員 read OK の例
create policy "public can read posts"
on posts for select
using (true);
-- サンプル
insert into posts (title, content) values ('hello', 'world');
取得
const { data, error } = await supabase
.from("posts")
.select("*")
if (error) console.error(error)
console.log(data) // [{ id, title, content, created_at }]
6. CLI(ローカル開発)
本番にいきなり書き込むより、ローカル Docker でフルスタックを立ち上げる方が安全。
インストール
# macOS
brew install supabase/tap/supabase
# その他
npm install -g supabase
初期化と起動
cd my-app
supabase init # supabase/ ディレクトリが作られる
supabase start # Docker で Postgres / Auth / Storage / Studio が起動
# URL / キーが表示される
# API URL: http://localhost:54321
# anon key: ...
# service: ...
# Studio: http://localhost:54323
主要コマンド
| コマンド | 用途 |
|---|---|
supabase start | ローカルスタック起動 |
supabase stop | 停止 |
supabase status | 状態確認 |
supabase db reset | マイグレーション全部適用してリセット |
supabase migration new <name> | マイグレーション作成 |
supabase db push | 本番にマイグレーション反映 |
supabase db pull | 本番のスキーマをローカルへ |
supabase link --project-ref xxxx | クラウドプロジェクトと紐付け |
supabase functions new my-fn | Edge Function 作成 |
supabase functions deploy my-fn | デプロイ |
supabase gen types typescript --linked | 型生成 |
7. 型を生成する(推奨)
テーブル定義から TypeScript の型を自動生成。これを使わないなら Supabase の旨みを活かしきれない。
supabase gen types typescript --linked > lib/database.types.ts
import { createClient } from "@supabase/supabase-js"
import type { Database } from "./database.types"
export const supabase = createClient<Database>(URL, KEY)
// 型推論が効く
const { data } = await supabase.from("posts").select("title")
data?.[0].title // string で推論される
CI で定期的に再生成するのが運用の定石。
8. Next.js (App Router) との統合
現代では @supabase/ssr を使うのが推奨。
npm install @supabase/ssr @supabase/supabase-js
Server Component で使う
// lib/supabase/server.ts
import { createServerClient } from "@supabase/ssr"
import { cookies } from "next/headers"
export async function createClient() {
const cookieStore = await cookies()
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() { return cookieStore.getAll() },
setAll(cookies) {
cookies.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options))
},
},
}
)
}
// page.tsx
const supabase = await createClient()
const { data } = await supabase.from("posts").select("*")
Client Component で使う
// lib/supabase/client.ts
"use client"
import { createBrowserClient } from "@supabase/ssr"
export const supabase = createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
)
middleware で session を refresh
// middleware.ts
import { type NextRequest, NextResponse } from "next/server"
import { createServerClient } from "@supabase/ssr"
export async function middleware(req: NextRequest) {
const res = NextResponse.next()
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() { return req.cookies.getAll() },
setAll(cookies) {
cookies.forEach(({ name, value, options }) =>
res.cookies.set(name, value, options))
},
},
}
)
await supabase.auth.getUser() // session を更新
return res
}
export const config = {
matcher: ["/((?!_next/static|_next/image|favicon.ico).*)"],
}
9. 共通エラー
| 症状 | 原因 |
|---|---|
| Invalid API key | URL / Key が違う / .env が読まれていない |
| row-level security policy violation | RLS でブロックされている。ポリシー作成必要 |
| JWT expired | セッション切れ。再ログイン or refresh |
| fetch failed (Edge / Cloudflare) | Node 互換問題。@supabase/supabase-js v2 の Web fetch を使う |
| CORS | API は CORS OK のはず。リバースプロキシ経由なら設定確認 |
10. ディレクトリ構成例
my-app/
├── supabase/
│ ├── config.toml # CLI の設定
│ ├── migrations/
│ │ └── 20260101120000_init.sql
│ ├── seed.sql
│ └── functions/
│ └── hello/
│ └── index.ts
├── lib/
│ ├── supabase/
│ │ ├── client.ts
│ │ └── server.ts
│ └── database.types.ts
├── app/ # Next.js
└── .env.local
最初にやるべき 5 つ
1) ローカル CLI で supabase start を動かす /
2) supabase migration new でスキーマを書く /
3) supabase gen types で型を生成 /
4) RLS を有効化してポリシーを 1 個書く /
5) supabase-js でクエリを 1 回成功させる。