スケーリングの壁:Firebaseが苦痛に変わる時
Firebaseは開発者にとって究極の「ハネムーン」と言えます。データベース、認証、ホスティングを10分足らずで構築できるのは、信じられないほど快適な体験です。
しかし、プロジェクトが成長するにつれて、その関係が悪化することが少なくありません。最初はシンプルなチャットアプリから始まりますが、突然、5つの異なるデータコレクションにまたがる結合(Join)が必要な複雑なダッシュボードをクライアントが要求してきます。FirestoreのようなNoSQLの世界では、クライアント側で処理するために数千件のレコードを取得してパフォーマンスを低下させるか、あちこちに重複したデータを保持するという煩雑な管理を強いられます。
最近、ある本番アプリを移行しましたが、比較的少数のユーザー数にもかかわらず、Firebaseの読み取りコストが月額400ドルに達していました。データの一貫性は悪夢のような状態でした。金曜の夜に、ユーザーのプロフィールのアバターは更新されたのに、投稿のアバターが古いままなのはなぜかとデバッグしたことがあるなら、あなたは「スケーリングの壁」に突き当たっています。
なぜリレーショナルデータはNoSQLで停滞するのか
問題はFirebaseの作りが悪いことではなく、構造化されたリレーショナルデータに対してNoSQLが誤ったツールであることが多い点にあります。ほとんどのアプリは本質的にリレーショナルです。ユーザーには投稿があり、投稿にはコメントがあり、コメントには「いいね」があります。
Firestoreでは、ネスト(入れ子)にするか、フラットなコレクションにするかという, 2つの不自由な選択を迫られます。ネストは独立したクエリをほぼ不可能にし、一方でフラットなコレクションはフロントエンド側で手動の「結合」コードを書く必要があります。このアーキテクチャの不一致は、以下の3つの苦痛を生みます。
- 冗長性のコスト(税金): 追加の読み取りを避けるために同じユーザー名を10個の異なるドキュメントに保存することになり、ストレージと帯域幅を浪費します。
- ゴーストデータ: 複雑でコストのかかるトランザクションなしに、10箇所の情報を一括更新して100%成功させることは困難です。
- セキュリティルールの肥大化: Firebaseのセキュリティルールは独自の仕様です。500行を超えると, 検索不能なブラックボックスとなり、監査するのが恐ろしくなります。
解決策:SQLの頭脳を持つBaaS의シンプルさ
Firebaseで対応できなくなった時の従来の解決策は、Node.jsやGoを使用し、PostgreSQLデータベースを組み合わせたカスタムバックエンドを構築することでした。これは完全な制御を可能にしますが、Firebaseのような「ライブ感」や、すぐに使える認証機能という生産性は失われます。また、サーバーインフラの管理という負担も引き受けることになります。
Supabaseは、よりスマートな「第三の道」を提示します。これは独自のクローズドな環境ではなく、PostgreSQL、認証用のGoTrue、Realtimeといったオープンソースの強力なツール群を一つの体験としてパッケージ化したものです。SQLの堅牢な構造と、モダンなBaaSの流れるような開発体験を同時に得ることができます。
Supabaseの柱:セキュリティとライブアップデート
Firebaseを置き換えるには、単なるテーブル構造以上のものが必要です。強固なセキュリティ層と、ユーザーに即座に更新をプッシュする機能が求められます。
1. PostgreSQLと行レベルセキュリティ(RLS)
独自のJSON風言語でルールを書く必要はありません。SupabaseはPostgreSQLのネイティブ機能である行レベルセキュリティ(RLS)を利用します。セキュリティはアプリケーションコードではなく、データベース内部に存在します。たとえ悪意のある者が公開APIキーを盗んだとしても、データベース自体が認証されたユーザーIDと一致しない行の返却を拒否します。
-- 例:シンプルで堅牢なセキュリティポリシー
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
CREATE POLICY "ユーザーは自身のデータのみ閲覧可能"
ON profiles FOR SELECT
USING (auth.uid() = id);
これは安心感という面で大きな利点です。セキュリティロジックは、クラウドプロバイダー独自のスクリプトではなく、30年以上の実績を持つデータベースエンジンであるPostgresによって処理されます。
2. スケーラブルなリアルタイムサブスクリプション
多くの開発者が、リアルタイム同期のためだけにFirebaseを使い続けています。Supabaseは、PostgreSQLのレプリケーションストリーム(WAL)を監視することでこれに対抗します。行が変更されると、サーバーはWebSocketを介してその変更差分をブロードキャストします。以下は、チャットや通知フィードの典型的な実装例です。
import { createClient } from '@supabase/supabase-client'
const supabase = createClient('PROJECT_URL', 'ANON_KEY')
const channel = supabase
.channel('room-1')
.on('postgres_changes',
{ event: 'INSERT', schema: 'public', table: 'messages' },
payload => console.log('新しいメッセージを受信しました!', payload)
)
.subscribe()
複雑なJOIN操作や全文検索といった機能を損なうことなく、瞬時のレスポンス性を手に入れることができます。
プロのヒント:移行前のデータクレンジング
移行はデータ負債を解消する絶好の機会です。NoSQLやレガシーシステムからの移行では、雑然としたCSVファイルに苦労することがよくあります。データを素早く準備するために、私は toolcraft.app/ja/tools/data/csv-to-json を使用しています。これはブラウザ上で完全に動作するため、機密データがローカルマシンから離れることはありません。レガシーなエクスポートデータをSupabaseのダッシュボードにインポートする前にフォーマットする最速の方法です。
より良い実装のブループリント
新規プロジェクトを開始する場合は、以下のワークフローに従って、バックエンドを長年管理可能な状態に保ちましょう。
- 最初にスキーマを定義する: 適当に進めないでください。外部キーを使用して参照整合性を維持します。これにより、NoSQLアプリを悩ませる「孤立データ(Orphan Data)」のバグを防ぐことができます。
- すぐに施錠する: RLSポリシーのないテーブルを放置しないでください。テーブルを作成した瞬間にRLSを有効にします。
- 型生成を利用する: SupabaseはスキーマからTypeScriptの型を自動生成できます。これにより、型のないFirebaseドキュメントを扱う際によく発生する「undefined is not a function」エラーを実質的に排除できます。
- ロジックをEdge Functionsにオフロードする: Stripeの支払い処理やサードパーティAPIの呼び出しなど、クライアント側で行うべきではないタスクにはSupabase Edge Functionsを使用します。これらは数秒でグローバルにデプロイされるサーバーレスのDeno関数です。
SQLの初期学習曲線はNoSQLよりもわずかに急ですが、長期的な見返りは膨大です。データの関係性が複雑になったからといって、アーキテクチャ全体を書き直す必要はありません。Supabaseは、素早く開始し、かつ正しくスケールさせるための架け橋となります。

