📝

Firebase Alternative な Supabase を使ってみた

2024/09/29に公開

個人サービスをノーコードで運営するお友達に「Next.js + Supabase に移行したい」と相談を受け、Supabase を軽く調べた&触ってみた。結論、良さそう。

軽く調べた

Supabase とは?

オープンソースの BaaS(Backend as a Service)サービス。Y combinator 出身で、2020年にリリース。

Firebase Alternative を謳い、クラウドで様々なバックエンド機能(認証・認可、データベース、ストレージ等)を提供している。

データベースは NoSQL ではなく オープンソースの PostgreSQL になっていて、SQL クエリによるデータ集計・フィルタリング、テキストデータの全文検索など PostgreSQL ならではの機能を活用できる点で人気・注目を集めていそう。

料金

Cloud Firestore(Firebase)PostgreSQL(Supabase) のデータベースのディスク容量で比較

  • Cloud Firestore(Firebase)
    • 最大で 1 GiB まで無料
    • それ以降は Google Cloud の料金を適用
  • PostgreSQL(Supabase)
    • 最大で 500 MB まで無料
    • 最大で 8 GM まで $25 / 月
    • それ以降は $0.125 / 1 GB

Supabase は無料プランの容量は少ないものの、8 GB まで定額制。Firebase は 1 GiB 以降は従量課金になるので、Supabase の方が料金の目安を立てやすそうです。

導入事例

有名企業だと、新しい居住体験を提供するスタートアップ企業の NOT A HOTEL さんが、 RAG アプリケーションの実装で Supabase を導入している。

RAG アプリケーションとは Retrieval-Augmented Generation の略で、LLM と外部のデータベースを結びつけるいわゆる AI・機械学習の分野の技術のことですが、Supabase の裏側は前述の通り PostgreSQL なので、 PostgresSQL の拡張機能である pgvector を使用してベクトル検索エンジンとして Supabase を活用されているとのこと。

https://twitter.com/dshukertjrjp/status/1793229253024399684

補足すると、GitHub のスター数は 72k で、GitHub Star History によるとリリース直後の傾きはゆるやかなものの、しばらくした後は Next.js 並のペースで伸びており、直近は rails のスター数を超えています。また、2022年に8000万ドルの資金調達のニュース、そしてつい先日シリーズCでさらに8000万ドルの資金調達のニュースが出ており、勢いを感じます。

軽く触った

前提

要件

バックエンドの機能は主に認証と決済で、お友達はデザイナーで React のスキルをお持ちです。今はノーコードを使っていて制約が多いので、UI/UX を自由に向上できる状態が理想です。領域を絞ったサブスクサービスで、アクセス数はそこまで多くない見込みです。初期の実装は相談に乗りつつ、お友達が自らサービス運用できる状態を目指します。

モック実装

会員登録、決済、有料コンテンツ表示、サブスクリプション管理までを仮実装して Supabase の使い勝手を試しました。

アーキテクチャ

お友達の運営するサービスを題材に、Next.js + Supabase をベースに具体的な技術構成を検討しました。

いわゆるサブスクサービスですが、しゅっと立ち上げられそう。

技術構成

アプリケーション

バックエンドは Supabase の BaaS 機能でクラウドベースで実現しつつ、フルスタック Next.js アプリケーションを Vercel にデプロイする構成にします。

API Routes でエンドポイントを生やし、ユーザーの認証・認可、リクエストのバリデーション、PostgreSQL のデータベースアクセスを実装します。

データベース

Supabase で PostgreSQL を使います。

ORM やマイグレーションツールに Prisma の利用を検討しましたが、Supabase CLI でテーブルのスキーマファイルによるマイグレーションのバージョン管理、supabase gen types typescript でスキーマファイルから TypeScript の型を自動生成までカバーできるので十分でした。

決済

Stripe の Checkout を使います。

Checkout とは、雑に言うと決済を Stripe に丸投げできる仕組みです。

サイト内に Stripe の決済フォームを実装せず、ユーザーには決済用のリンクを発行して Stripe のサイトで決済を完了していただき、Stripe の提供する Webhook 等で決済の成功や失敗のステータスを同期する構成がベストプラクティスかと思います。

データベースに決済に関する情報をほとんど持たなくて済むので、セキュリティの向上が嬉しいです。

参考:Next.js + Supabase + Checkout を使った公式デモアプリ

https://github.com/vercel/nextjs-subscription-payments

メール配信

メール配信サービスの Resend を使います。

Supabase は自前で SMTP サーバーを用意する必要があります。一応無料枠があるのですが、きびしい通数制限で、開発段階でも実質必須でした。ただ、Supabase 公式の Resend Integration アプリを使うと簡単に設定でき、ケアはされています

認証・認可

Supabase の Auth を使います。

Social Auth と Phone Auth もサポートされているので、必要に応じて拡張できます。

Supabase では Supabase client を使うことで、データベースのデータの読み書きをフロントで簡単に実装できる一方で、セキュリティ的に注意が必要です。

// Supabase client を作成

import { createBrowserClient } from '@supabase/ssr';

export const createClient = () =>
  createBrowserClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL!,
    process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
  );
// Supabase client を使用してデータベースのデータを読み書き

import { createClient } from '@/utils/supabase/client';

const supabase = createClient();

const { data, error } = await supabase
  .from('subscriptions')
  .select('id')
  .eq('user_id', userId)
  .single();

このコードの場合、ブラウザのリクエスト URL は以下のようになります。また、リクエストの際に必要となる API キーは js ファイルから辿ることで取得が可能です。

curl https://[supabaseId].supabase.co/rest/v1/subscriptions?select=id&user_id=eq.[userId] -H "apiKey: [apiKey]"

そのため、例えば以下のように容易に全てのサブスクリプション情報を取得するクエリに変更してリクエストすることができてしまいます。

// user_id の where 句を削除

curl https://[supabaseId].supabase.co/rest/v1/subscriptions?select=* -H "apiKey: [apiKey]"

この問題は、根本的には PostgreSQL の RLS(行レベルセキュリティ)で対策が可能です。auth.uid() を使用して認証されているユーザーの uuid を参照でき、RLS を利用したアクセス制御を簡潔に記述できる仕組みになっています。Supabase の RLS について詳しくはこちら

create table subscriptions (
    id uuid primary key default gen_random_uuid(),
    user_id uuid NOT NULL REFERENCES auth.users(id),
    stripe_subscription_id VARCHAR(255) NOT NULL,
    stripe_invoice_id VARCHAR(255) UNIQUE,
    start_timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
    end_timestamp TIMESTAMP WITH TIME ZONE NOT NULL
);

alter table subscriptions
  enable row level security;

create policy "read"
  on subscription
  for select
	using (auth.uid() = user_id);
	  
...

スタイリング

Tailwind CSS を使う予定です。

ヘッドレス UI ライブラリの React Area の使用を少し検討しました。

従来の UI ライブラリにありがちだった「ビジュアル」と「振る舞い」の密結合の問題の解消できるとお聞きして興味を持ったのですが、今回は振る舞いをそこまでリッチにする必要がある?というと No だったので、依存するライブラリを増やしたくない気持ちがまさって見送りました。

感想

Good

  • 使い勝手の良い BaaS を使用して、プロジェクトをスモールスタートできる
    • Firebase と比べてコストを見積りやすい
    • PostgreSQL ベースの信頼性の高さ
    • 行レベルセキュリティによるアクセス制御の簡便さ
    • Supabase CLI の開発者体験の高さ
    • クラウドの管理画面の見やすさ、使いやすさ

Bad

  • 基本機能を中心に試した程度ですが、BaaS としての使い勝手に不満は少なく、あるとすると当たり前に Supabase への依存が発生する
    • Supabase 独自機能、Supabase client や Supabase CLI の多用による他プラットフォーム・他ツール移行の柔軟性低下

さいごに

個人開発、小規模プロジェクトには良さそう。

自前でデータベースを管理でき、スケールを目指すプロジェクトの場合は、慎重に。

今回 Next.js + TypeScript でバックエンドを開発する技術構成を試してみて、API Router + tRPC で API スキーマを End-to-End でタイプセーフにするアプローチなど良さそうだったので、また試してみたいと思います。

以上、お友達にいい感じの相談を受け、秋分の日の3連休で Supabase のキャッチアップをしてみた、でした。なるべくアウトプットしていくぞい。

参考

Discussion