Database & Migration

【データベース設計】Supabase×Alembicで実現するローカルプレイヤーの国別メタデータ拡張

ユーザー登録不要の匿名対戦ゲームにおいて、世界のプレイヤーの対戦であることを表現するために国旗・国名メタデータをDBモデルへ安全に追加・移行したAlembic実践ログです。

1. はじめに:匿名対戦における国情報表示のねらい

Doodle Fighterは「会員登録ゼロ・即プレイ開始」を売りにしています。

ランキング画面や対戦画面で「🇯🇵 日本 の 描画ファイター」や「🇺🇸 アメリカ の 挑戦者」といった国旗アイコンが表示されると、非同期対戦の臨場感が一気に増します。

2. データベースモデルの拡張(SQLAlchemy & Pydantic)

既存の `players` テーブルに対し、ISO国コード(例: `JP`, `US`, `FR`)を保持するカラム `country_code` を安全に追加しました。

SQLAlchemy モデル (backend/models.py)

# backend/models.py
class Player(Base):
    __tablename__ = "players"

    id = Column(String, primary_key=True)
    nickname = Column(String, nullable=False)
    country_code = Column(String(5), nullable=True, default="JP")  # 新規追加
    created_at = Column(DateTime(timezone=True), server_default=func.now())

3. Alembicによる安全なマイグレーション手順

運用中のSupabase(PostgreSQL)環境でデータを保持したままカラムを追加するため、Alembicマイグレーションスクリプトを作成・実行しました。

Alembic スクリプト (alembic/versions/f7c87faaa133_add_country_code_to_players.py)

"""add_country_code_to_players

Revision ID: f7c87faaa133
Create Date: 2026-07-16 23:36:22
"""
from alembic import op
import sqlalchemy as sa

def upgrade():
    op.add_column('players', sa.Column('country_code', sa.String(length=5), nullable=True, server_default='JP'))

def downgrade():
    op.drop_column('players', 'country_code')

4. フロントエンドでの国旗絵文字変換(JavaScript)

国コード(`JP` 等)をユニコード絵文字の国旗(🇯🇵)に変換するロジックをフロントエンドに実装しました。国コードの各文字から `0x1F1E6` オフセットを加算するだけで絵文字化が可能です。

// frontend/static/js/app.js
function getFlagEmoji(countryCode) {
    if (!countryCode || countryCode.length !== 2) return '🌐';
    const codePoints = countryCode
        .toUpperCase()
        .split('')
        .map(char => 127397 + char.charCodeAt(0));
    return String.fromCodePoint(...codePoints);
}

5. まとめ

匿名プレイの気軽さを保ちながら、メタデータ(国情報)をスキーマに追加することで、Web対戦の賑わいと没入感を大幅に向上させることができました。