
Vercel+Supabaseでリアルタイムマルチプレイヤークイズゲームを作った
はじめに
Next.js と Supabase を組み合わせると、リアルタイム通信を含むマルチプレイヤーゲームを少ないコード量で構築できます。本記事では、子どもの 1 歳の誕生日パーティー用に作成した写真クイズゲームの実装を通して、その具体的な手法を紹介します。
Vercel とは
Vercel は Next.js の開発元が提供するホスティングプラットフォームです。Git リポジトリと連携するだけで、ビルドからデプロイまでを自動で行えます。
Supabase とは
Supabase は PostgreSQL をベースにした BaaS (Backend as a Service) です。データベース、認証、ストレージ、リアルタイム通信といったバックエンド機能を SDK 経由で手軽に利用できます。
対象読者
- Next.js でのアプリケーション開発経験がある方
- Supabase のリアルタイム機能に興味がある方
参考
- Next.js Documentation
- Supabase Documentation
- Supabase Realtime - Postgres Changes
- Supabase Realtime - Presence
- Vercel Integration for Supabase
作ったものの全体像
参加者がスマートフォンからルームコードで同じゲームに参加し、子どもの月齢を当てるクイズに 3 回挑戦してスコアを競うマルチプレイヤーゲームです。

ゲームの流れは下記のような形です。
フロントエンドとして Next.js 16 (App Router) + React 19 + TypeScript を、バックエンドとして Supabase (PostgreSQL + Realtime + Storage) を使い、Vercel にデプロイしています。フロントエンドからバックエンドまで TypeScript で統一しています。
Supabase に準備したもの
ゲームで使う写真は Supabase Storage に保管しました。photos という公開バケットを作成し、月齢ごとにフォルダを分けてアップロードしました。
photos/
├── 0/ # 0ヶ月目の写真
├── 1/ # 1ヶ月目の写真
...
└── 11/ # 11ヶ月目の写真
アップロード用のスクリプトを用意し、ローカルの写真フォルダを指定してバケットへのアップロードと photos テーブルへのメタデータ登録をまとめて行うようにしました。
データベースのスキーマと RLS ポリシーは supabase/schema.sql に定義しています。テーブルは photos、rooms、room_players、room_questions、votes、vote_counts の 6 つです。正解情報を含む room_questions と投票詳細の votes は RLS でクライアントからの直接アクセスを遮断し、API Routes からのみ操作します。また、votes への INSERT をトリガーで vote_counts に集約し、Supabase Realtime ではこの集約テーブルだけを配信することで、投票内容を隠しつつ投票数だけをリアルタイムに共有しています。
実装したもの
アプリケーションの構成を簡単に紹介します。
API Routes: ルーム作成・参加、ゲーム開始 (出題生成)、投票、正解発表、次の問題、ルーム削除の各エンドポイントを用意しています。ホスト権限の検証や投票の二重防止 (UNIQUE 制約) はすべてサーバー側で処理します。
カスタムフック: useRoom でゲーム状態の変化を、useVoteCounts で投票数の変化を、それぞれ Supabase Realtime の Postgres Changes で購読しています。ロビー画面では usePresence で Supabase の Presence API を使い、接続中のプレイヤーをリアルタイムに表示します。
Supabase クライアントの使い分け: ブラウザ用 (公開キー、RLS に従う) とサーバー用 (秘密キー、RLS を迂回) の 2 種類を用意し、クライアントにはデータの参照とリアルタイムサブスクリプションだけを、サーバーには正解の照合やスコア更新を担当させています。
Vercel へのデプロイ
Vercel には Supabase との Integration が用意されています。Vercel のプロジェクト設定から Supabase Integration を追加し、Supabase アカウントと連携するだけで、SUPABASE_URL、SUPABASE_ANON_KEY、SUPABASE_SECRET_KEY が自動的に環境変数として設定されます。手動で環境変数をコピーする必要がないため、設定ミスのリスクを減らせます。
- Vercel のダッシュボード で Git リポジトリをインポート

- プロジェクトの Settings → Integrations から「Supabase」を追加

- Supabase アカウントで認証し、対象プロジェクトと連携


これだけで環境変数が自動設定され、再デプロイすれば Supabase に接続された状態で動作します。
プレイしてみた
実際にゲームをプレイした様子を紹介します。
ルーム作成とロビー
トップページでニックネームを入力してルームを作成すると、6 桁のルームコードが発行されます。参加者がコードを入力して参加するとリアルタイムにプレイヤー一覧が更新され、全員が揃ったらホストがゲームを開始します。

出題と投票
2 枚の写真が左右に表示され、制限時間 20 秒以内に正しい月齢の写真をタップして投票します。投票後は他のプレイヤーの投票状況がリアルタイムに表示されます。

正解発表と結果
制限時間の終了後、ホストが正解を発表します。全 3 問が終了すると正解数のランキングが紙吹雪のアニメーションとともに表示されます。

分かったこと
Vercel と Supabase の Integration は想像以上に手軽
数クリックだけで Supabase の環境変数が同期されるのはとても便利だと感じました。ちなみに、Supabase 側でキーをローテーションした際にも自動で反映されます。ちょっとしたイベント向けアプリを素早く立ち上げたいケースに適していると思いました。
トリガー + Realtime でリアルタイム機能を安全に構築できる
WebSocket サーバーを自前で立てずにリアルタイム通信を構築できたのがありがたかったです。PostgreSQL のトリガーで投票を集約テーブルに反映し、その集約テーブルだけを Realtime で配信するパターンは、個々のデータを隠しつつ集計結果だけを共有したい場面で広く応用できそうです。
まとめ
Vercel と Supabase を組み合わせることで、リアルタイム通信を含むマルチプレイヤーゲームを Next.js だけで構築できました。イベント向けのちょっとしたアプリケーションであれば、この構成で十分に対応できると感じました。








