N.N. LLC. ロゴ - 千葉県船橋市のIT企業N.N. LLC.
技術ブログ

Supabaseで認証・DB・ストレージを一括管理 — Firebaseからの移行も解説

18分で読めます
Supabaseで認証・DB・ストレージを一括管理 — Firebaseからの移行も解説

はじめに — Supabaseが変えるバックエンド開発

Webアプリケーションを構築する際、認証・データベース・ストレージ・リアルタイム通信といったバックエンド機能は欠かせません。しかし、これらをゼロから構築するには膨大な時間とコストがかかります。Supabaseは、これらの機能をオープンソースで提供するBaaS(Backend as a Service)であり、PostgreSQLをベースとした堅牢なバックエンド基盤を数分で立ち上げることができます。

N.N. LLC.のコーポレートサイトでも、お問い合わせ管理、ブログシステム、管理画面のバックエンドとしてSupabaseを採用しています。本記事では、実プロジェクトの経験をもとに、Supabaseの全機能を網羅的に解説します。

Supabaseの全体像 — 「オープンソースのFirebase代替」の実力

Supabaseは2020年にローンチされ、2024年時点でGitHub Stars数は75,000を超えています。「Firebase Alternative」を標榜していますが、その内部アーキテクチャはまったく異なります。

  • PostgreSQL: データベースエンジンはPostgreSQL 15+。リレーショナルDBの柔軟性と信頼性を享受できる
  • PostgREST: PostgreSQLスキーマから自動的にRESTful APIを生成
  • GoTrue: 認証・認可サービス(JWT発行、OAuth対応)
  • Realtime: PostgreSQLの変更をWebSocketで配信
  • Storage: S3互換のオブジェクトストレージ
  • Edge Functions: Deno Runtimeで動作するサーバーレス関数

Firebaseとの最大の違いは、データベースがNoSQL(Firestore)ではなくRDB(PostgreSQL)である点です。複雑なJOINクエリ、トランザクション、外部キー制約など、RDBの強力な機能をフル活用できます。

認証機能の実装 — Email/Password、OAuth、Magic Link

Supabase Authは、GoTrueをベースとした認証サービスです。主要な認証方式をすべてサポートしています。

Email/Password認証

最もシンプルな認証方式です。Supabaseクライアントを使えば、数行で実装できます。

import { createClient } from '@supabase/supabase-js'

const supabase = createClient(
  process.env.NEXT_PUBLIC_SUPABASE_URL!,
  process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)

// ユーザー登録
const { data, error } = await supabase.auth.signUp({
  email: 'user@example.com',
  password: 'secure-password-123'
})

// ログイン
const { data, error } = await supabase.auth.signInWithPassword({
  email: 'user@example.com',
  password: 'secure-password-123'
})

OAuth認証(Google、GitHub等)

Google、GitHub、Twitter、Discord、Slackなど30以上のOAuthプロバイダーに対応しています。ダッシュボードでクライアントIDとシークレットを設定するだけで利用可能です。

// Google OAuth認証
const { data, error } = await supabase.auth.signInWithOAuth({
  provider: 'google',
  options: {
    redirectTo: 'https://your-site.com/auth/callback'
  }
})

Magic Link認証

パスワードレス認証の一種で、メールアドレスにワンタイムリンクを送信します。ユーザーはパスワードを覚える必要がなく、UXが大幅に向上します。

// Magic Linkの送信
const { data, error } = await supabase.auth.signInWithOtp({
  email: 'user@example.com',
  options: {
    emailRedirectTo: 'https://your-site.com/dashboard'
  }
})

Row Level Security(RLS)— テーブルレベルのアクセス制御

RLSはPostgreSQLのネイティブ機能で、行レベルのアクセス制御を実現します。Supabaseでは、RLSが全テーブルに対してデフォルトで有効化されており、ポリシーを定義しない限りデータにアクセスできません。

これにより、クライアントサイドから直接データベースにクエリを発行しても、認可されたデータのみが返される安全な設計が可能になります。

公開データのポリシー例

-- ブログ記事は誰でも閲覧可能
CREATE POLICY "公開記事は誰でも閲覧可能" ON nnllc.blog_posts
  FOR SELECT
  USING (status = 'published');

-- サービス情報は誰でも閲覧可能
CREATE POLICY "サービス情報は公開" ON nnllc.services
  FOR SELECT
  USING (is_active = true);

認証済みユーザー向けポリシー例

-- 管理者のみが記事を作成・編集可能
CREATE POLICY "管理者のみ記事を編集可能" ON nnllc.blog_posts
  FOR ALL
  USING (
    EXISTS (
      SELECT 1 FROM nnllc.admin_users
      WHERE id = auth.uid()
      AND role IN ('admin', 'editor')
    )
  );

カスタムスキーマの活用パターン

Supabaseのデフォルトはpublicスキーマですが、N.N. LLC.では独自のnnllcスキーマを作成して利用しています。カスタムスキーマには以下のメリットがあります。

  • 名前空間の分離: Supabase内部テーブル(auth.users等)との名前衝突を回避
  • 論理的なグループ化: アプリケーション固有のテーブルを明確に分離
  • マイグレーション管理: スキーマ単位でのバックアップ・リストアが容易
  • セキュリティ: スキーマレベルの権限管理が可能
-- カスタムスキーマの作成
CREATE SCHEMA IF NOT EXISTS nnllc;

-- PostgRESTにスキーマを公開(APIアクセスに必要)
ALTER ROLE authenticator SET pgrst.db_schemas TO 'public,nnllc';
NOTIFY pgrst, 'reload schema';

-- Supabaseクライアントからカスタムスキーマにアクセス
const supabase = createClient(url, key, {
  db: { schema: 'nnllc' }
})

注意点として、PostgRESTにカスタムスキーマを認識させるために、authenticatorロールの設定変更が必要です。また、RLSポリシーもカスタムスキーマのテーブルに対して個別に設定します。

Supabase Storage — 画像・ファイル管理

Supabase StorageはS3互換のオブジェクトストレージです。画像、動画、PDFなど、あらゆるファイルをアップロード・管理できます。

バケットの作成とアップロード

// パブリックバケット(画像配信用)
const { data, error } = await supabase.storage
  .from('blog-images')
  .upload('posts/hero.webp', file, {
    contentType: 'image/webp',
    cacheControl: '3600'
  })

// 公開URLの取得
const { data } = supabase.storage
  .from('blog-images')
  .getPublicUrl('posts/hero.webp')

画像の変換(Image Transformations)

Supabase Storageには画像変換機能が組み込まれています。サムネイルの生成やリサイズをサーバーサイドで処理できます。

// 幅400pxにリサイズした画像のURLを取得
const { data } = supabase.storage
  .from('blog-images')
  .getPublicUrl('posts/hero.webp', {
    transform: { width: 400, height: 300, resize: 'cover' }
  })

Edge Functions — Deno Runtimeのサーバーレス関数

Supabase Edge FunctionsはDeno Runtimeで動作するサーバーレス関数です。外部API連携、Webhook処理、複雑なビジネスロジックの実行に適しています。

// supabase/functions/send-notification/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()

  // お問い合わせ受信時にSlack通知を送信
  await fetch(Deno.env.get('SLACK_WEBHOOK_URL')!, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      text: `新しいお問い合わせ: ${record.name}様 (${record.email})`
    })
  })

  return new Response(JSON.stringify({ success: true }), {
    headers: { 'Content-Type': 'application/json' }
  })
})

Edge Functionsはデータベーストリガーと組み合わせることで、特定のテーブルへのINSERT/UPDATE時に自動実行させることも可能です。

Firebaseからの移行ステップ

Firebaseからの移行を検討している方のために、段階的な移行手順を紹介します。

Step 1: データモデルの変換

FirestoreのNoSQLドキュメント構造を、PostgreSQLのリレーショナルモデルに変換します。ネストされたサブコレクションは、外部キーで関連付けたテーブルに分割します。

Step 2: 認証の移行

Firebase AuthenticationのユーザーデータをSupabase Authにインポートします。Supabaseはパスワードハッシュ付きでのユーザーインポートをサポートしているため、ユーザーに再登録を求める必要はありません。

Step 3: ストレージの移行

Firebase Cloud Storageのファイルをダウンロードし、Supabase Storageにアップロードします。バケット構造は事前に設計し、RLSポリシーも移行先で設定します。

Step 4: クライアントコードの書き換え

FirebaseのSDK呼び出しをSupabaseのクライアントライブラリに置き換えます。クエリの構文は異なりますが、基本的なCRUD操作は直感的に移行できます。

Step 5: リアルタイム機能の移行

Firestoreのリアルタイムリスナーを、Supabase Realtimeのサブスクリプションに置き換えます。チャネルベースのリアルタイム通信により、より柔軟な設計が可能になります。

Next.jsとの統合パターン — SSR/SSG対応

Supabaseは@supabase/ssrパッケージにより、Next.jsのServer ComponentsとRoute Handlersでのサーバーサイド利用を公式にサポートしています。

Server Componentでのデータ取得

// app/blog/page.tsx(Server Component)
import { createServerComponentClient } from '@supabase/ssr'
import { cookies } from 'next/headers'

export default async function BlogPage() {
  const cookieStore = await cookies()
  const supabase = createServerComponentClient(cookieStore)

  // サーバーサイドでブログ記事を取得
  const { data: posts } = await supabase
    .from('blog_posts')
    .select('id, title, slug, excerpt, published_at')
    .eq('status', 'published')
    .order('published_at', { ascending: false })

  return (
    <div>
      {posts?.map(post => (
        <article key={post.id}>
          <h2>{post.title}</h2>
          <p>{post.excerpt}</p>
        </article>
      ))}
    </div>
  )
}

Client Componentでのリアルタイム更新

// 管理画面でのリアルタイム監視
'use client'
import { useEffect, useState } from 'react'
import { createBrowserClient } from '@supabase/ssr'

export function InquiryMonitor() {
  const [inquiries, setInquiries] = useState([])
  const supabase = createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
  )

  useEffect(() => {
    // リアルタイムでお問い合わせを監視
    const channel = supabase
      .channel('inquiries')
      .on('postgres_changes', {
        event: 'INSERT',
        schema: 'nnllc',
        table: 'inquiries'
      }, (payload) => {
        setInquiries(prev => [payload.new, ...prev])
      })
      .subscribe()

    return () => { supabase.removeChannel(channel) }
  }, [])

  return <div>{/* お問い合わせ一覧 */}</div>
}

まとめ — Supabaseは「次世代のバックエンド標準」になるか

Supabaseは、PostgreSQLの堅牢性とBaaSの手軽さを両立した、現時点で最もバランスの取れたバックエンドプラットフォームです。特にNext.jsとの統合は非常にスムーズで、フルスタックWebアプリケーションの開発効率を大幅に向上させます。

認証、データベース、ストレージ、リアルタイム、Edge Functionsと、バックエンドに必要な機能が一通り揃っており、Firebaseからの移行先としても有力な選択肢です。オープンソースであるため、将来的にセルフホスティングに切り替えることも可能です。

バックエンド構築の選択肢でお悩みの方は、まずSupabaseの無料プランで小さなプロジェクトから試してみることをお勧めします。PostgreSQLの強力さと、モダンなBaaSの利便性を実感できるはずです。

Supabase
PostgreSQL
Next.js
RLS
BaaS

関連記事