Kusakabe Pages

KMPにおけるクラウドデータ永続化(BaaS)の選択肢 (2026-07-25)

1. なぜBaaSが必要になるのか

現在のJiuJitsuScoreBoardアプリはスタンドアロン(1台完結)で、データはすべてインメモリに保持している。 しかし将来的に、以下のような機能を追加する場合はクラウド側のバックエンドが必要になる。

これらを自前のサーバーで一から構築するのは個人開発ではコストが高すぎるため、BaaS(Backend as a Service) を利用するのが現実的。


2. BaaSの選択肢比較

KMPプロジェクトで利用可能なBaaSは、主に以下の2つが候補となる。

Firebase(Firestore)

Googleが提供するBaaS。Android開発では最もメジャーな選択肢。

Supabase

PostgreSQLベースのオープンソースBaaS。「Firebaseのオープンソース代替」として急成長中。

比較まとめ

観点 Firebase Supabase
KMP公式SDK ❌ なし(コミュニティ製ラッパー) ❌ なし(コミュニティ製だが非常に充実)
DB NoSQL (Firestore) SQL (PostgreSQL)
リアルタイム同期 ✅ Firestore Realtime ✅ Realtime (WebSocket)
認証 ✅ Firebase Auth ✅ Supabase Auth
Ktor との親和性 △(ラッパー経由) ◎(Ktor エンジンを直接利用)
ベンダーロックイン 高い(Google依存) 低い(セルフホスト可能)
日本語情報の多さ ◎ 豊富 △ まだ少なめ

3. KMPプロジェクトにおける推奨

KMPで新規にクラウド連携を構築するなら、Supabase + supabase-kt が現時点では最も相性が良い。理由は以下の通り。

  1. 既存のKMPエコシステムとの一貫性: Ktor、kotlinx.serialization、Coroutines/Flow など、既にアプリで使っている(または今後使う)技術スタックとシームレスに統合できる。
  2. SDKの成熟度: supabase-kt はBOM(Bill of Materials)によるバージョン管理、型安全なDSL、ネイティブ認証(Android Credential Manager / Apple Sign-In)など、実用的な機能を備えている。
  3. SQLベース: アプリ内DBとして Room や SQLDelight(どちらもSQLベース)を採用した場合、クラウド側もSQLであるSupabaseの方がデータモデルの設計に一貫性が出る。

ただし、既にFirebaseの経験があるチームや、FCM(プッシュ通知)を多用する要件がある場合は、Firebase + GitLive SDKも十分に選択肢になり得る。


4. Supabase導入時のコード例(イメージ)

// Supabase クライアントの初期化 (commonMain)
val supabase = createSupabaseClient(
    supabaseUrl = "https://your-project.supabase.co",
    supabaseKey = "your-anon-key"
) {
    install(Postgrest)  // DB操作
    install(Auth)       // 認証
    install(Realtime)   // リアルタイム同期
}

// データの取得(型安全)
@Serializable
data class MatchHistory(
    val id: Int,
    val winnerName: String,
    val score: String
)

suspend fun getMatchHistory(): List<MatchHistory> {
    return supabase.from("match_history")
        .select()
        .decodeList<MatchHistory>()
}

// リアルタイム購読(スコアの変更を監視)
val channel = supabase.channel("match-updates")
val changeFlow = channel.postgresChangeFlow<PostgresAction.Update>(schema = "public") {
    table = "live_match"
}
changeFlow.collect { change ->
    println("スコアが更新されました: ${change.record}")
}
channel.subscribe()

参考資料(一次情報)