「Firebaseは便利だけど、ベンダーロックインが心配」「SQLデータベースを使いたいのにFirestoreのNoSQL設計に苦労している」「バックエンドをなるべく書かずにアプリを開発したい」――こうした悩みを抱える開発者にとって、Supabaseは最適な選択肢です。
Supabaseは「Firebaseのオープンソースオルタナティブ」を掲げるBaaS(Backend as a Service)プラットフォームで、PostgreSQLを基盤としています。本記事では、Supabaseのセットアップから認証、データベース操作、リアルタイム機能まで、実際のコードを交えて初心者向けに解説します。
Supabaseとは?Firebaseとの比較
Supabaseは、PostgreSQL、認証、リアルタイムサブスクリプション、ストレージ、Edge Functionsなどの機能を統合したBaaSプラットフォームです。
Supabaseの構成要素
- PostgreSQL:フル機能のリレーショナルデータベース
- GoTrue:認証サーバー(メール/パスワード、OAuth、マジックリンク)
- PostgREST:PostgreSQLからRESTful APIを自動生成
- Realtime:WebSocketベースのリアルタイム変更通知
- Storage:S3互換のファイルストレージ
- Edge Functions:Deno ベースのサーバーレス関数
- pg_graphql:PostgreSQLからGraphQL APIを自動生成
Firebaseとの主な違い
データベース:FirebaseのFirestoreはNoSQL(ドキュメント型)ですが、SupabaseはPostgreSQL(リレーショナル)です。JOINや複雑なクエリが使え、SQLの知識がそのまま活かせます。
オープンソース:Supabaseは全コンポーネントがオープンソースで、セルフホスティングが可能です。ベンダーロックインのリスクがありません。
Row Level Security:SupabaseはPostgreSQLのRLS(行レベルセキュリティ)を使って、データベースレベルでアクセス制御を行います。
移行の容易さ:標準的なPostgreSQLなので、他のホスティングサービスへの移行が容易です。
プロジェクトのセットアップ
Supabaseプロジェクトの作成
supabase.comでアカウントを作成し、新しいプロジェクトを作成します。プロジェクト作成時にデータベースのパスワードを設定します(後から必要になるので控えておきましょう)。
Next.jsプロジェクトとの連携
# Next.jsプロジェクトの作成
npx create-next-app@latest my-supabase-app --typescript --tailwind --app
cd my-supabase-app
# Supabaseクライアントのインストール
npm install @supabase/supabase-js @supabase/ssr
// .env.local
NEXT_PUBLIC_SUPABASE_URL=https://xxxxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIs...
// lib/supabase/client.ts(ブラウザ用)
import { createBrowserClient } from '@supabase/ssr';
export function createClient() {
return createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
}
// 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(cookiesToSet) {
cookiesToSet.forEach(({ name, value, options }) => {
cookieStore.set(name, value, options);
});
},
},
}
);
}
TypeScript型定義の自動生成
# Supabase CLIのインストール
npm install -D supabase
# ログイン
npx supabase login
# 型定義の生成
npx supabase gen types typescript --project-id your-project-id > lib/database.types.ts
// 型安全なクライアントの作成
import { createBrowserClient } from '@supabase/ssr';
import type { Database } from '@/lib/database.types';
export function createClient() {
return createBrowserClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
);
}
認証(Authentication)の実装
SupabaseはGoTrueベースの認証機能を提供し、メール/パスワード、OAuth(Google、GitHub等)、マジックリンクなど多様な認証方式に対応しています。
メール/パスワード認証
// サインアップ
async function signUp(email: string, password: string) {
const supabase = createClient();
const { data, error } = await supabase.auth.signUp({
email,
password,
options: {
data: {
display_name: '田中太郎', // メタデータ
},
},
});
if (error) {
console.error('サインアップエラー:', error.message);
return null;
}
return data.user;
}
// ログイン
async function signIn(email: string, password: string) {
const supabase = createClient();
const { data, error } = await supabase.auth.signInWithPassword({
email,
password,
});
if (error) {
console.error('ログインエラー:', error.message);
return null;
}
return data.session;
}
// ログアウト
async function signOut() {
const supabase = createClient();
await supabase.auth.signOut();
}
// セッション情報の取得
async function getSession() {
const supabase = createClient();
const { data: { session } } = await supabase.auth.getSession();
return session;
}
OAuth認証(Google)
// Google OAuthログイン
async function signInWithGoogle() {
const supabase = createClient();
const { data, error } = await supabase.auth.signInWithOAuth({
provider: 'google',
options: {
redirectTo: `${window.location.origin}/auth/callback`,
queryParams: {
access_type: 'offline',
prompt: 'consent',
},
},
});
}
// app/auth/callback/route.ts(OAuth コールバック)
import { NextRequest, NextResponse } from 'next/server';
import { createClient } from '@/lib/supabase/server';
export async function GET(request: NextRequest) {
const { searchParams } = new URL(request.url);
const code = searchParams.get('code');
if (code) {
const supabase = await createClient();
await supabase.auth.exchangeCodeForSession(code);
}
return NextResponse.redirect(new URL('/dashboard', request.url));
}
認証状態に基づくミドルウェア
// middleware.ts
import { createServerClient } from '@supabase/ssr';
import { NextResponse, type NextRequest } from 'next/server';
export async function middleware(request: NextRequest) {
let supabaseResponse = NextResponse.next({ request });
const supabase = createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() {
return request.cookies.getAll();
},
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value, options }) => {
request.cookies.set(name, value);
supabaseResponse.cookies.set(name, value, options);
});
},
},
}
);
const { data: { user } } = await supabase.auth.getUser();
// 未認証ユーザーをログインページにリダイレクト
if (!user && request.nextUrl.pathname.startsWith('/dashboard')) {
const url = request.nextUrl.clone();
url.pathname = '/login';
return NextResponse.redirect(url);
}
return supabaseResponse;
}
export const config = {
matcher: ['/dashboard/:path*', '/settings/:path*'],
};
データベース操作(CRUD)
SupabaseのJavaScriptクライアントは、PostgRESTを通じてPostgreSQLに対して直感的なCRUD操作を提供します。
テーブルの作成(SQL Editor)
-- Supabaseダッシュボードの SQL Editor で実行
CREATE TABLE posts (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
title VARCHAR(255) NOT NULL,
content TEXT,
slug VARCHAR(255) UNIQUE NOT NULL,
published BOOLEAN DEFAULT false,
author_id UUID REFERENCES auth.users(id) ON DELETE CASCADE NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW(),
updated_at TIMESTAMPTZ DEFAULT NOW()
);
-- RLS(Row Level Security)を有効化
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
-- ポリシー:公開済み記事は誰でも読める
CREATE POLICY "Published posts are visible to everyone"
ON posts FOR SELECT
USING (published = true);
-- ポリシー:自分の記事のみ作成可能
CREATE POLICY "Users can create their own posts"
ON posts FOR INSERT
WITH CHECK (auth.uid() = author_id);
-- ポリシー:自分の記事のみ更新可能
CREATE POLICY "Users can update their own posts"
ON posts FOR UPDATE
USING (auth.uid() = author_id);
-- ポリシー:自分の記事のみ削除可能
CREATE POLICY "Users can delete their own posts"
ON posts FOR DELETE
USING (auth.uid() = author_id);
クライアントからのCRUD操作
import { createClient } from '@/lib/supabase/client';
const supabase = createClient();
// CREATE:記事の作成
async function createPost(title: string, content: string, slug: string) {
const { data, error } = await supabase
.from('posts')
.insert({
title,
content,
slug,
author_id: (await supabase.auth.getUser()).data.user?.id,
})
.select()
.single();
return { data, error };
}
// READ:記事一覧の取得
async function getPosts() {
const { data, error } = await supabase
.from('posts')
.select('id, title, slug, created_at')
.eq('published', true)
.order('created_at', { ascending: false })
.limit(10);
return { data, error };
}
// READ:リレーション付きで取得
async function getPostWithAuthor(slug: string) {
const { data, error } = await supabase
.from('posts')
.select(`
*,
author:profiles!author_id (
display_name,
avatar_url
)
`)
.eq('slug', slug)
.single();
return { data, error };
}
// UPDATE:記事の更新
async function updatePost(id: string, updates: { title?: string; content?: string }) {
const { data, error } = await supabase
.from('posts')
.update({
...updates,
updated_at: new Date().toISOString(),
})
.eq('id', id)
.select()
.single();
return { data, error };
}
// DELETE:記事の削除
async function deletePost(id: string) {
const { error } = await supabase
.from('posts')
.delete()
.eq('id', id);
return { error };
}
リアルタイム機能の実装
Supabaseのリアルタイム機能を使えば、データベースの変更をリアルタイムでクライアントに通知できます。
リアルタイムサブスクリプション
// Reactコンポーネントでのリアルタイムサブスクリプション
'use client';
import { useEffect, useState } from 'react';
import { createClient } from '@/lib/supabase/client';
export function RealtimePosts() {
const [posts, setPosts] = useState<any[]>([]);
const supabase = createClient();
useEffect(() => {
// 初期データの取得
async function fetchPosts() {
const { data } = await supabase
.from('posts')
.select('*')
.eq('published', true)
.order('created_at', { ascending: false });
if (data) setPosts(data);
}
fetchPosts();
// リアルタイムサブスクリプションの設定
const channel = supabase
.channel('posts-changes')
.on(
'postgres_changes',
{
event: '*', // INSERT, UPDATE, DELETE すべて
schema: 'public',
table: 'posts',
filter: 'published=eq.true',
},
(payload) => {
console.log('Change received:', payload);
if (payload.eventType === 'INSERT') {
setPosts((prev) => [payload.new, ...prev]);
} else if (payload.eventType === 'UPDATE') {
setPosts((prev) =>
prev.map((post) =>
post.id === payload.new.id ? payload.new : post
)
);
} else if (payload.eventType === 'DELETE') {
setPosts((prev) =>
prev.filter((post) => post.id !== payload.old.id)
);
}
}
)
.subscribe();
// クリーンアップ
return () => {
supabase.removeChannel(channel);
};
}, []);
return (
<ul>
{posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
);
}
リアルタイムを有効にする設定
-- Supabase ダッシュボードの SQL Editor で実行
-- テーブルのリアルタイムを有効化
ALTER PUBLICATION supabase_realtime ADD TABLE posts;
ストレージ(ファイルアップロード)
バケットの作成とアップロード
-- ストレージバケットの作成(SQL Editor)
INSERT INTO storage.buckets (id, name, public)
VALUES ('avatars', 'avatars', true);
-- アップロードのポリシー
CREATE POLICY "Users can upload their own avatar"
ON storage.objects FOR INSERT
WITH CHECK (
bucket_id = 'avatars'
AND auth.uid()::text = (storage.foldername(name))[1]
);
-- 公開アクセスのポリシー
CREATE POLICY "Anyone can view avatars"
ON storage.objects FOR SELECT
USING (bucket_id = 'avatars');
// ファイルのアップロード
async function uploadAvatar(userId: string, file: File) {
const supabase = createClient();
const fileExt = file.name.split('.').pop();
const filePath = `${userId}/avatar.${fileExt}`;
const { data, error } = await supabase.storage
.from('avatars')
.upload(filePath, file, {
cacheControl: '3600',
upsert: true, // 既存ファイルを上書き
});
if (error) {
console.error('アップロードエラー:', error.message);
return null;
}
// 公開URLの取得
const { data: { publicUrl } } = supabase.storage
.from('avatars')
.getPublicUrl(filePath);
return publicUrl;
}
Edge Functionsの活用
Edge FunctionsはDeno ベースのサーバーレス関数で、WebhookやAPIエンドポイントとして利用できます。
# Edge Functionの作成
npx supabase functions new send-welcome-email
// supabase/functions/send-welcome-email/index.ts
import { serve } from 'https://deno.land/std@0.177.0/http/server.ts';
import { createClient } from 'https://esm.sh/@supabase/supabase-js@2';
serve(async (req) => {
const { record } = await req.json();
// Supabase管理者クライアント
const supabase = createClient(
Deno.env.get('SUPABASE_URL')!,
Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!
);
// ウェルカムメールの送信(外部メールサービスを利用)
const response = await fetch('https://api.resend.com/emails', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${Deno.env.get('RESEND_API_KEY')}`,
},
body: JSON.stringify({
from: 'noreply@example.com',
to: record.email,
subject: 'ようこそ!アカウント登録が完了しました',
html: `${record.raw_user_meta_data?.display_name}さん、登録ありがとうございます。
`,
}),
});
return new Response(JSON.stringify({ success: true }), {
headers: { 'Content-Type': 'application/json' },
});
});
# デプロイ
npx supabase functions deploy send-welcome-email
# ローカルテスト
npx supabase functions serve send-welcome-email
まとめ
本記事では、Supabaseの基本から実践的な活用方法まで解説しました。
- データベース:PostgreSQLベースで、SQLの知識がそのまま活かせる
- 認証:メール/パスワード、OAuth、マジックリンクに対応
- RLS:行レベルセキュリティでデータベースレベルのアクセス制御
- リアルタイム:データベースの変更をWebSocketでクライアントに通知
- ストレージ:S3互換のファイルストレージ
- Edge Functions:Denoベースのサーバーレス関数
- 型安全:CLIで型定義を自動生成し、TypeScriptと組み合わせて安全に開発
Supabaseは、PostgreSQLの強力な機能とBaaSの手軽さを両立したプラットフォームです。Firebaseのようなスピード感で開発しつつ、SQLの柔軟性とオープンソースの安心感を手に入れることができます。無料プランで十分に試せるので、まずは小さなプロジェクトから始めてみてください。
関連記事
AIエージェント開発入門|自律型AIの仕組みと構築方法を解説【2026年版】
AI駆動コーディングワークフロー|Claude Code・Cursor・Copilotの実践的使い分け
プロンプトエンジニアリング上級編|Chain-of-Thought・Few-Shot・ReActの実践
APIレート制限の設計と実装|トークンバケット・スライディングウィンドウ解説
APIバージョニング戦略|URL・ヘッダー・クエリパラメータの使い分け
BIツール入門|Metabase・Redash・Looker Studioでデータ可視化する方法
チャットボット開発入門|LINE Bot・Slack Botの構築方法と活用事例
CI/CDパイプラインの基礎|継続的インテグレーション・デリバリーの全体像