SuperBase — アプリ概要
SuperBase は、Supabase にインスパイアされた Backend-as-a-Service (BaaS) プラットフォーム です。
サーバーレス関数の作成・実行と、PostgreSQL データベースのビジュアル管理を1つのダッシュボードで提供します。
AWS Lambda や Supabase を使わなくても、コードを書いて即実行・DBをGUIで操作
できる開発者向けプラットフォームです。
ターゲット
- バックエンド開発を素早く始めたい開発者 — Lambda の設定なしでサーバーレス関数を即実行
- フルスタック開発者 — DB管理と関数実行を1画面で完結
- プロトタイピング・ハッカソン参加者 — インフラ構築なしで高速イテレーション
- 非エンジニアのチームメンバー — SQLを書かずにスプレッドシート感覚でDB操作
技術構成・アーキテクチャ図
- フロントエンド: Next.js 16 (App Router), React 19, TypeScript 5, Tailwind CSS 4, shadcn/ui
- API ゲートウェイ: Go 1.25, Gin (REST → gRPC 変換)
- マイクロサービス: Go, gRPC, Protocol Buffers
- ORM / DB: GORM + PostgreSQL 16
- 関数実行基盤: Docker SDK (Alpine コンテナで隔離実行)
- インフラ: Docker Compose (5サービス構成)
- パッケージマネージャ: Bun
- リンター: Biome
サービスアーキテクチャ

遊びごころポイント
ターゲットに刺さる機能
1. サーバーレス関数のワンクリック実行
コードを書いて「Invoke」ボタンを押すだけで、隔離されたDockerコンテナ内で即座に実行。
stdout(緑)/ stderr(赤)がリアルタイムで表示され、実行時間・終了コードも一目でわかる。
2. スプレッドシート風データベースエディタ
テーブル一覧 → 行データ → カラム定義 → SQL実行まで、タブを切り替えるだけで全操作が完結。
行をクリックするとサイドパネルがスライドインし、インライン編集が可能。
3. プロジェクトダッシュボード
Supabase そっくりの UI で、APIキー表示・SDK スニペットコピー・最近のアクティビティログを一覧表示。
組織名「Keyaki Studio」、プロジェクト「acorn-prod」、リージョン「Tokyo」など、本物さながらのプロジェクト管理体験。
4. 対応ランタイム
- Python 3.12 (
python:3.12-alpine)
- Node.js 20 (
node:20-alpine)
ランタイム追加は Docker イメージ名のマッピングを1行足すだけで拡張可能。
技術的遊び
1. Docker ソケットマウントによるサンドボックス実行
# docker-compose.yaml
functions:
volumes:
- /var/run/docker.sock:/var/run/docker.sock
ホストの Docker デーモンを共有し、Docker-in-Docker を避けつつ完全隔離されたコンテナ実行 を実現。
各ユーザー関数はシブリングコンテナとして独立して動作する。
2. Docker ストリームの手動デマルチプレクス
// Docker の多重化ストリームヘッダーを手動パース
// 8バイトヘッダー: 1(タイプ) + 3(パディング) + 4(サイズ)
func demuxLogs(r io.Reader, stdout, stderr *bytes.Buffer) error {
hdr := make([]byte, 8)
for {
io.ReadFull(r, hdr)
size := int(hdr[4])<<24 | int(hdr[5])<<16 | int(hdr[6])<<8 | int(hdr[7])
buf := make([]byte, size)
io.ReadFull(r, buf)
switch hdr[0] {
case 1: stdout.Write(buf) // stdout
case 2: stderr.Write(buf) // stderr
}
}
}
stdout/stderr を正確に分離してフロントエンドに返す、低レベルなストリーム処理を自前実装。
3. Proto-First 開発(契約駆動設計)
.proto ファイルでサービス契約を先に定義し、コード生成してから実装。
型安全な gRPC 通信と、バイナリ Protobuf シリアライゼーションによる高効率な通信を実現。
4. Context ベースのタイムアウト制御
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
defer cancel()
1つの Go context がコンテナ作成・起動・待機すべてに伝播し、タイムアウトを一元管理。
5. バイナリ行エンコーディング
gRPC の bytes 型で行データを転送。JSON 文字列ではなくバイナリエンコードにより、大量データ転送時のワイヤサイズを最適化。
アプリ画面
ダッシュボード(ホーム)
- プロジェクトステータスカード(名前・リージョン・PostgreSQLバージョン・稼働状態)
- APIキー表示(anon key / service role key / 接続文字列)
- SDK スニペット(JavaScript/TypeScript のコピー可能なコード例)
- 最近のアクティビティフィード(INSERT、認証、ファイルアップロード等のタイムライン)
Functions(サーバーレス関数)
- 左サイドバー: 関数一覧 + 検索 + 新規作成ボタン
- メイン: コードエディタ(Code タブ)/ 実行結果(Output タブ)/ 設定(Settings タブ)
- ヘッダー: 関数名・ランタイムタグ・作成日・Invoke ボタン
- ミニ統計: タイムアウト値 / 最終終了コード / 実行時間
Editor(データベース管理)
- 左サイドバー: スキーマ切替(public, auth, storage)+ テーブル一覧(行数・RLSバッジ付き)
- メイン: スプレッドシート風テーブルビュー(型バッジ・主キーアイコン・NULL制約表示)
- 右パネル: 行のインライン編集フォーム
- タブ: Data / Definition(カラム編集)/ SQL(クエリ実行)
Settings
- General / API / Database / Members / Billing / Danger Zone のタブ構成
API Docs
- インタラクティブな REST API リファレンス(エンドポイント一覧・リクエスト例・cURL コピー)
苦労したことや大変だったこと
本番環境や、インフラなどの構築で最新のブランチなどが反映されないなど、主に本番環境にデプロイする部分で苦労しました。
挑戦・成長したところ
- マイクロサービスアーキテクチャの実践
REST ゲートウェイ + gRPC マイクロサービスという本格的なアーキテクチャを、ハッカソンの限られた時間で構築。
Protocol Buffers による契約駆動設計を採用し、フロントエンドとバックエンドの並行開発を実現した。
- Docker SDK を使ったコンテナ実行基盤
ユーザーコードを安全に実行するため、Docker SDK を直接操作するサンドボックス実行基盤を自前実装。
タイムアウト制御・ストリームデマルチプレクス・コンテナクリーンアップなど、低レベルな処理に挑戦した。
- Go のレイヤードアーキテクチャ
Model → Repository → Usecase → Server の4層構造で統一。
依存性注入(DI)を活用し、テスタブルで保守性の高い設計を実践した。
- モダンフロントエンド技術の採用
Next.js 16 (App Router) + React 19 + TypeScript 5 + Tailwind CSS 4 という最新スタックを採用。
shadcn/ui によるコンポーネント設計で、Supabase に匹敵する洗練された UI を構築した。
チームワーク・チーム開発で工夫したところ
- チームで知らない技術などを勉強し合いながらプロジェクトを完成させたこと
マイクロサービスなど知らないチームメンバーなどが居る中で、工夫しながらプロジェクトを作成したこと。
- Docker Compose による開発環境の統一
make build && make up の1コマンドで全サービスが起動する環境を整備。
「自分の環境では動く」問題を排除し、チーム全員が同一環境で開発できるようにした。
- 明確なディレクトリ構成と責務分離
各サービスが同じ4層構造(model → repository → usecase → server)を持つことで、
誰がどのサービスを担当しても迷わない 統一的なコード構造を実現した。
- Git ブランチ戦略とコミット規約
- ブランチ名:
- コミット:
- PR は main ブランチにマージ、Closes #N で Issue 自動クローズ
- コード生成による型安全な連携
make proto で gRPC コードを自動生成し、gen/ ディレクトリに配置。
手動編集を禁止することで、サービス間の型の不整合を防止した。
- Feature モジュール構成(フロントエンド)
frontend/src/features/ 配下に機能単位でモジュールを分割(functions, editor, home, settings, auth,
api-docs)。
複数人が同時に異なる機能を開発しても コンフリクトが最小限 になる構成にした。
インフラいろいろ
- Functions Pod が 0/1 Running で Ready にならない
- 症状: functions-569cb74866-tp9j4 0/1 Running — Pod
は起動しているが Ready にならない
- 原因: k8s.yml に gRPC
ヘルスチェック(readinessProbe /
livenessProbe)を設定したが、Go のコード側に
grpc.health.v1.Health サービスの実装がなかった
- 難しさ: k8s のプローブ設定とアプリケーション側の実
装が揃っていないと動かないという、インフラとバックエ
ンドの境界の問題。Running なのに Ready
にならないという状態が初見では分かりにくい
- 古い ReplicaSet が残り続けて複数世代の Pod が混在
- 症状: 3つの異なる ReplicaSet(569cb74866,
67cb95f68c, 84f5c479c4)から Pod が立ち上がり、古い
Pod が CrashLoopBackOff
- 原因: 過去のロールアウトが完了しなかったため、古い
ReplicaSet が Pod
を持ったまま残っていた。古い世代は旧
DATABASE_URL(user=dev database=functions_db
hostname=postgres)を参照しており DB 接続に失敗
- 難しさ: Deployment を何度も更新すると過去のリビジ
ョンが残る仕組みを理解する必要がある。kubectl scale
replicaset --replicas=0
で手動クリーンアップが必要だった
- ノードにキャッシュされた古いイメージが使われる(I
magePullBackOff の亜種)
- 症状: ECR に新しいイメージを push したのに、Pod
が古いイメージで起動する。Events に Container image
"...back-functions:latest" already present on
machine
- 原因: imagePullPolicy がデフォルト(IfNotPresent)
のため、ノードにキャッシュされた :latest
イメージが再利用された
- 解決: k8s.yml に imagePullPolicy: Always を追加
- 難しさ: :latest タグでも自動で pull されない。push
したのに反映されないという一見不可解な挙動
- 環境変数名の不一致 — NEXT_PUBLIC_API_URL vs
NEXT_PUBLIC_GATEWAY_URL
- 症状: フロントエンドが localhost:8080 に API
リクエストを送ってしまう
- 原因: frontend/k8s.yml では NEXT_PUBLIC_API_URL
を設定しているが、コード(constants.ts)は
NEXT_PUBLIC_GATEWAY_URLを参照。変数が見つからずフォールバックの
localhost:8080 が使われた
- 難しさ: 変数名が1文字違うだけでフォールバックに落
ちる。ローカル開発では localhost:8080
で動くので気づきにくい
- NEXT_PUBLIC_*
はランタイムではなくビルド時にインライン化される
- 症状: k8s の env
で正しい値を設定しても、ブラウザからは依然
localhost:8080 にリクエストが飛ぶ
- 原因: Next.js の NEXT_PUBLIC_*
環境変数はビルド時(bun run
build)にJSバンドルにハードコードされる。Pod のコン
テナ環境変数として渡してもランタイムでは反映されない
- 解決: Dockerfile に ARG NEXT_PUBLIC_GATEWAY_URL +
ENV を追加し、docker build --build-arg
でビルド時に注入
- 難しさ: Next.js
固有の仕様。インフラ側だけでは解決できず、Dockerfile
の変更が必要
- Gateway Service のポート不一致 — ELB が :8080
で待ち受け
- 症状: フロントエンドから Gateway への API
リクエストが接続拒否(ERR_CONNECTION_REFUSED)
- 原因: Gateway の k8s Service が port: 8080
だったため ELB は :8080
でリッスン。しかしフロントエンドの URL
にはポート指定なし → ブラウザは :80 に送信 →
到達しない
- 解決: Gateway Service の port を 80 に、targetPort
を 8080 に変更
- 難しさ: ELB の外部ポートとコンテナの内部ポートの違
いを意識する必要がある
- Gateway URL の先頭 a 欠落 — DNS 解決失敗
- 症状: ブラウザで Cross-Origin Request
Blocked、Status code: (null)、NetworkError when
attempting to fetch resource
- 原因: フロントエンドに焼き込まれた URL が
http://7736a536... だったが、実際の ELB ホスト名は
http://a7736a536...(先頭に a がある)。DNS
解決自体が失敗していた
- 難しさ: エラーメッセージが「CORS」と出るが、実際は
DNS 解決失敗。CORS エラーに見えて実はネットワーク到
達性の問題というミスリード。ELB
のホスト名は長くて手動コピー時にミスしやすい
- Gateway の Dockerfile に storage
モジュールのコピーが不足
- 症状: docker build が失敗 — open
/build/services/storage/go.mod: no such file or
directory
- 原因: main に新しく追加された services/storage
モジュールを Gateway の go.mod
が参照しているが、Dockerfile でコピーしていなかった
- 難しさ: Go の replace
ディレクティブによるローカルモジュール依存は、Docker
ビルドコンテキストに全て含める必要がある。チームメ
ンバーがコードを追加すると Dockerfile
も更新しないとビルドが壊れる
- Functions 実行時に Docker デーモンに接続できない
- 症状: execute API が 500 — Cannot connect to the
Docker daemon at unix:///var/run/docker.sock
- 原因: Functions Service はコード実行時に Docker
SDK でコンテナを起動するが、EKS の Pod
内にはDockerデーモンが存在しない(containerd
ランタイム)
- 解決:
docker:dind(Docker-in-Docker)をサイドカーコンテナ
として追加し、DOCKER_HOST=tcp://localhost:2375
を設定
- 難しさ: ローカルの docker-compose 環境では Docker
ソケットが使えるが、k8s 環境では使えない。開発環境と
本番環境のランタイムの違いを理解する必要がある
- 「進化論」機能による関数の自動 DEACTIVATED
- 症状: 一度実行した関数が DEACTIVATED
になり、2回目以降 FailedPrecondition で実行できない
- 原因: コードに「10秒間に3回以下の呼び出しだと関数
を無効化する」機能が実装されていた。テストで1回だけ
呼ぶと10秒後に絶滅する
- 難しさ: インフラ側の問題ではなくアプリケーションロ
ジックだが、デプロイ後のテストで突然動かなくなるため
、インフラの問題と切り分けにくい
総括
1週間でこれらを全て経験・解決したのは相当な密度。特
に難しかったのは:
- エラーメッセージが原因と一致しない(CORS エラー →
実は DNS 解決失敗、Running → 実は Ready ではない)
- ローカルと k8s の環境差異(Docker
ソケット、Next.js のビルド時変数、imagePullPolicy)
- 複数レイヤーが絡む問題(フロントエンドのビルド ×
Dockerfile × k8s env × ELB ポート × DNS ホスト名)