SupabaseをNext.jsポートフォリオに組み込んだ話|FirebaseからDB移行した13時間の記録

ポートフォリオにSupabase(PostgreSQL BaaS)を導入しFirebaseから移行した全記録。RLSの設定ミスで2時間溶かした失敗談と、Next.js・TypeScriptとの連携方法を実際のコードで解説します。

SupabaseポートフォリオFirebaseNext.jsTypeScriptPostgreSQL初心者

※当サイトはアフィリエイトプログラムに参加しています。記事内のリンクから商品を購入すると、当サイトに報酬が支払われることがあります。詳しくはプライバシーポリシーをご覧ください。

「3月末の土曜日、ポートフォリオを大幅リニューアルしよう」と思い立って始めた作業が、気づいたら深夜2時になっていました。

ここまで読んでくれた人に正直に言うと、Supabaseの導入そのものより、RLSの設定ミスを直すのに2時間溶かしたのが最大の山でした。

(この記事を書いている間にコーヒーが2杯冷めた)

この記事では、FirebaseからSupabaseへの移行を13時間かけてやり遂げた一部始終を書きます。コードも全部貼るので、同じ轍を踏まないために参考にしてみてください。


BaaSの選択肢を考えるエンジニアのイメージ

なぜ今さらFirebaseからSupabaseに乗り換えたか

先に結論を言うと、「SQLを実務レベルで書けるかどうか」が採用に影響すると感じたからです。

もともと僕のポートフォリオはFirebaseのFirestoreを使っていました。データの読み書きは簡単で、認証もGoogleログインをサクッと実装できる。「便利なFirebase」はとても優秀だったんですが、3月に面談したとある会社のCTOに「FirestoreしかやってないとRDBMSの経験が薄く見えることがある」と言われて、そこから気になり始めました。

正直、当時の僕は「え、Firestoreって使いやすいし何がダメなの」と思っていました。でもよく考えたら、業務システムの多くはPostgreSQLやMySQLで動いていて、フロントエンドエンジニアでも「SQLくらいは読める」のが当然視されている部分があります。

SupabaseはPostgreSQLをバックエンドに持つBaaS(Backend as a Service)です。FirebaseのようにNoCodeに近い感覚で使えつつ、中身はちゃんとSQLが使える。「手を動かしながらRDBMSを学ぶ」という名目で乗り換えを決めました。

年度末の忙しい時期に始めたのは完全に僕の判断ミスでしたが、結果的にはやってよかったです。


実際の移行手順:Next.js × TypeScript × Supabaseの組み合わせ

1. Supabaseプロジェクトの作成

supabase.comでアカウントを作って、プロジェクトを新規作成します。

リージョンは「East Asia (Tokyo)」を選べるので、日本向けサービスならここを選ぶのがレイテンシ的にベターです。DBのパスワードはしっかり管理してください(.env.localに書いてGit管理外にするのは当然として)。

2. SDKのインストールと型の自動生成

npm install @supabase/supabase-js
npm install --save-dev supabase

# Supabase CLIでDBの型定義を自動生成
npx supabase gen types typescript --project-id <your-project-id> > types/supabase.ts

ここがポイントなんですが、型定義の自動生成は必ず最初にやっておくことをおすすめします。後からやると、すでに書いたコードを全部型定義に合わせて直すことになります。正直、これを後回しにして1時間無駄にしました。

TypeScriptの型システムをある程度理解していると、この恩恵がより実感できます。

3. クライアントの初期化

// lib/supabase.ts
import { createClient } from '@supabase/supabase-js'
import type { Database } from '@/types/supabase'

const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL!
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!

export const supabase = createClient<Database>(supabaseUrl, supabaseAnonKey)

型引数にDatabaseを渡すことで、テーブルへのクエリが型安全になります。from('profiles')と書いた時点で、そのテーブルのカラム名が補完されるのは気持ちいい体験でした。

4. データの読み書き

// ユーザー一覧を取得
const { data, error } = await supabase
  .from('profiles')
  .select('id, name, bio')
  .order('created_at', { ascending: false })

// データを追加
const { data: inserted, error: insertError } = await supabase
  .from('profiles')
  .insert({ name: 'Souma', bio: 'フロントエンドエンジニア' })
  .select()

Firebaseに比べてクエリが直感的に書けるのは好印象でした。order()filter()がメソッドチェーンでつながっていく感覚は、REST APIの設計思想に近いものがあります。


データベースとフロントエンドの接続イメージ

ハマったポイント3つ(ここ書くのに30分悩んだ)

① RLSを有効にしたら全データが見えなくなった【2時間の沼】

Supabaseには**Row Level Security(RLS)**という機能があります。要は「どのユーザーがどのデータを見ていいか」を行レベルで制御する仕組みです。

問題は、RLSをデフォルトで有効にすると、ポリシーを設定しない限りデータが全件取れなくなることです。

最初は「データが取れない、バグだ」と30分くらい悩みました。その後も「ポリシーを設定したはずなのになんで見えないんだ」で1時間半。合計で2時間くらいここに費やしました。

-- 全員が閲覧できるポリシーを設定する例
CREATE POLICY "Public profiles are viewable by everyone."
  ON profiles FOR SELECT
  USING (true);

-- ログインユーザーのみが自分のデータを更新できるポリシー
CREATE POLICY "Users can update own profile."
  ON profiles FOR UPDATE
  USING (auth.uid() = id);

ちょっと話が逸れるんですが、このRLSという概念、実際の業務システムでも「なぜこのユーザーにはこのデータが見えないんだ」という問題の根本原因になりやすいです。SupabaseでRLSを学んでおくと、業務での権限設計の理解にも繋がります。

SQL・データベースの基礎をある程度知っていれば、もっと早く気づけたポイントでした。

② 型定義ファイルの再生成を忘れる

DBのスキーマを変更したあと、型定義ファイルを再生成しないとTypeScriptが古い型を参照し続けます。これで30分くらい「なんでこの型エラーが消えないんだ」となりました。

スキーマを変えたら必ずnpx supabase gen typesを実行する、をルーティン化するのがおすすめです。

③ 無料枠の制限で焦った

Supbaseの無料枠は7日間非アクティブだとプロジェクトが一時停止されます。ポートフォリオのDBが止まっていて面接中にデモができない、という最悪のシナリオは避けたいところです。

週1回は触るか、GitHub Actionsで定期実行するなどの対策が有効です。僕は簡単なAPIヘルスチェックをActions経由で毎日叩くようにして解決しました。


Supabaseをおすすめしない人・場面

これ、ポジティブな記事でも必ず書くと決めています。「全肯定レビュー」を信用する人はいないので。

こういう人・場面にはFirebaseのほうが向いていると思います:

  • 最速でリリースしたい: Firestoreのほうがセットアップが速い。RLSの設定でハマる時間が省ける
  • リアルタイム同期がメイン: FirebaseのonSnapshot()はリアルタイム性が高く、Supabaseのrealtimeより安定している印象
  • SQLを書く気がない: SupabaseのUIは便利ですが、本質的にはSQLを理解しないと詰まります
  • モバイルアプリも作る: FirebaseのFlutter/iOS/Android対応は圧倒的に成熟している

逆に「SQLを学びながらBaaSも使いたい」「ポートフォリオでRDBMSの経験をアピールしたい」という目的なら、Supabaseは最適です。


よくある質問

Q: Supabaseは完全無料で使えますか?

A: 無料プランがあり、個人のポートフォリオや学習用途であれば無料で十分です。ただし、7日間アクティビティがないとプロジェクトが一時停止されるので、定期的にアクセスする仕組みを作っておくことをおすすめします。有料プランは$25/月〜です。本文では触れませんでしたが、Storageも無料枠で1GBまで使えます。

Q: FirebaseからSupabaseへの移行はどのくらい時間がかかりますか?

A: 僕の場合、小規模なポートフォリオ(テーブル3つ、認証あり)で約13時間かかりました。RLSの設定や型定義の整備に想定外の時間がかかります。事前にドキュメントをしっかり読んでから始めると5〜8時間程度に短縮できると思います。

Q: Supabaseの認証(Auth)はFirebaseと比べてどうですか?

A: どちらも似た機能を持っていますが、Supabaseの方が設定がやや複雑です。Googleログインなどのソーシャル認証はどちらも対応しています。Next.jsとの組み合わせには@supabase/auth-helpers-nextjsを使うと認証状態の管理がかなり楽になります。

Q: ポートフォリオにSupabaseを使うと評価が上がりますか?

A: 一概には言えませんが、「RDBMSを実際に触った経験がある」と示せる点は評価されやすいです。ただし使っているだけでなく、RLSや型安全なクエリなど「なぜそう設計したか」を説明できるようにしておくことが重要です。

Q: AWSのRDSやDynamoDBとどう違うの?

A: RDSは自分でサーバーを管理する必要があり、ポートフォリオには過剰です。SupabaseはPostgreSQLをマネージドサービスとして提供しているので、DB自体のサーバー管理が不要です。AWS入門記事でAWSのサービス体系を把握してから比較すると理解しやすいと思います。


まとめ

Supabaseは「Firebaseの便利さ」と「PostgreSQLの本格感」が両立したサービスです。

ポートフォリオにDBを組み込みたいエンジニアには、学習コスト込みでも十分にやる価値があります。特にNext.js × TypeScriptとの相性は良く、型安全なDB操作が実装できます。

もしポートフォリオの作り方で悩んでいる段階であれば、Supabaseを前提にしたDB設計から始めるのも一つの選択肢です。

フロントエンドのデプロイ環境についてはNext.js + Tailwind CSSのセットアップ記事も参考にしてみてください。


【2026年4月追記】Supabase Storage も試しました

記事を書いた後、プロフィール画像のアップロード機能を実装するためにSupabase Storageも触ってみました。

S3と似たAPIで操作でき、画像の公開URL生成やアクセス制御もRLSで管理できます。AWSのS3経験がある人は概念がすんなり入ると思います。画像アップロード実装はまた別の記事でまとめる予定です。

あわせて読みたい

【買い切り型】Colosoでプロから学ぶ

業界トップの講師によるオンライン講座。買い切り型で何度でも復習可能。受講期限なし。

講座一覧を見る →