「すーぱー」べーす

https://github.com/KinuGra/giraffe-2604

Go

Kubernetes

AWS

Terraform

RDS

SuperなBase

zatu

fe01

kfff

長谷川航大

upq6784ieoo

推しアイデア

推し

作った背景

背景

推し技術

技術

プロジェクト詳細

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

サービスアーキテクチャ

image image

遊びごころポイント

ターゲットに刺さる機能

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 コピー)

苦労したことや大変だったこと

本番環境や、インフラなどの構築で最新のブランチなどが反映されないなど、主に本番環境にデプロイする部分で苦労しました。


挑戦・成長したところ

  1. マイクロサービスアーキテクチャの実践 REST ゲートウェイ + gRPC マイクロサービスという本格的なアーキテクチャを、ハッカソンの限られた時間で構築。 Protocol Buffers による契約駆動設計を採用し、フロントエンドとバックエンドの並行開発を実現した。
  2. Docker SDK を使ったコンテナ実行基盤 ユーザーコードを安全に実行するため、Docker SDK を直接操作するサンドボックス実行基盤を自前実装。 タイムアウト制御・ストリームデマルチプレクス・コンテナクリーンアップなど、低レベルな処理に挑戦した。
  3. Go のレイヤードアーキテクチャ Model → Repository → Usecase → Server の4層構造で統一。 依存性注入(DI)を活用し、テスタブルで保守性の高い設計を実践した。
  4. モダンフロントエンド技術の採用 Next.js 16 (App Router) + React 19 + TypeScript 5 + Tailwind CSS 4 という最新スタックを採用。 shadcn/ui によるコンポーネント設計で、Supabase に匹敵する洗練された UI を構築した。

チームワーク・チーム開発で工夫したところ

  1. チームで知らない技術などを勉強し合いながらプロジェクトを完成させたこと マイクロサービスなど知らないチームメンバーなどが居る中で、工夫しながらプロジェクトを作成したこと。
  2. Docker Compose による開発環境の統一 make build && make up の1コマンドで全サービスが起動する環境を整備。 「自分の環境では動く」問題を排除し、チーム全員が同一環境で開発できるようにした。
  3. 明確なディレクトリ構成と責務分離 各サービスが同じ4層構造(model → repository → usecase → server)を持つことで、 誰がどのサービスを担当しても迷わない 統一的なコード構造を実現した。
  4. Git ブランチ戦略とコミット規約
  • ブランチ名:
  • コミット:
  • PR は main ブランチにマージ、Closes #N で Issue 自動クローズ
  1. コード生成による型安全な連携 make proto で gRPC コードを自動生成し、gen/ ディレクトリに配置。 手動編集を禁止することで、サービス間の型の不整合を防止した。
  2. Feature モジュール構成(フロントエンド) frontend/src/features/ 配下に機能単位でモジュールを分割(functions, editor, home, settings, auth, api-docs)。 複数人が同時に異なる機能を開発しても コンフリクトが最小限 になる構成にした。

インフラいろいろ

  1. 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 にならないという状態が初見では分かりにくい
  1. 古い 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 で手動クリーンアップが必要だった
  1. ノードにキャッシュされた古いイメージが使われる(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 したのに反映されないという一見不可解な挙動
  1. 環境変数名の不一致 — 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 で動くので気づきにくい
  1. 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 の変更が必要
  1. 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 の外部ポートとコンテナの内部ポートの違 いを意識する必要がある
  1. 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 のホスト名は長くて手動コピー時にミスしやすい
  1. 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 も更新しないとビルドが壊れる
  1. 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 環境では使えない。開発環境と 本番環境のランタイムの違いを理解する必要がある
  1. 「進化論」機能による関数の自動 DEACTIVATED
  • 症状: 一度実行した関数が DEACTIVATED になり、2回目以降 FailedPrecondition で実行できない
  • 原因: コードに「10秒間に3回以下の呼び出しだと関数 を無効化する」機能が実装されていた。テストで1回だけ 呼ぶと10秒後に絶滅する
  • 難しさ: インフラ側の問題ではなくアプリケーションロ ジックだが、デプロイ後のテストで突然動かなくなるため 、インフラの問題と切り分けにくい

総括

1週間でこれらを全て経験・解決したのは相当な密度。特 に難しかったのは:

  • エラーメッセージが原因と一致しない(CORS エラー → 実は DNS 解決失敗、Running → 実は Ready ではない)
  • ローカルと k8s の環境差異(Docker ソケット、Next.js のビルド時変数、imagePullPolicy)
  • 複数レイヤーが絡む問題(フロントエンドのビルド × Dockerfile × k8s env × ELB ポート × DNS ホスト名)

zatu

@zatu