Go バックエンドのDB実装(Bun ORM・マイグレーション)
Bunでtime.TimeをPostgreSQLのdate型として扱う
約2か月前
Go の Bun ORM では time.Time / *time.Time フィールドをそのまま定義すると PostgreSQL 側で timestamp/timestamptz として schema 生成されることがある。日付だけを保存したいカラムでは、Bun model のフィールドに bun:",type:date" を明示し、HTTP/API 層では YYYY-MM-DD 文字列として入出力、usecase/domain 以降では *time.Time として扱うと境界が明確になる。
golang-migrate の dirty 状態を安全に復旧する手順
約2か月前
golang-migrate が Dirty database version ... で停止した場合は、まず dirty と表示された migration が実体変更を残しているかを DB 上で確認する。変更対象のテーブルや index が存在しないなど未適用と判断できる場合は、migrate force <直前の正常 version> で schema_migrations を戻してから migration を再実行する。実体変更が残っている場合は、手動でロールバックまたは整合を取ってから force する。
Bun UpdateQuery で Column と Set を混在させる時の注意
約2か月前
Bun の UpdateQuery で Column(...) と Set(...) を組み合わせると、生成 SQL が Set(...) で指定した式だけになり、Column(...) で指定したモデル列が SET に入らない場合がある。モデル値と式を同時に更新したい場合は、更新対象列も Set("column = ?", value) で明示するか、生成 SQL を確認してから実装する。
MySQL の UNIQUE 制約は NULL を含む重複を許可する
約2か月前
MySQL では UNIQUE KEY の対象カラムに NULL が含まれる場合、複数行が同じ非 NULL 値を持っていても NULL 部分は重複扱いされない。複数の条件タイプを nullable カラムで表すテーブルでは、複合 UNIQUE だけでは「同じ条件の重複」を防げないことがある。この場合は、生成カラムや条件別テーブル、またはアプリケーション/ドメイン層での明示的な重複検証を検討する。
Bun の m2m リレーションは全ての DB 初期化経路で中間テーブルを RegisterModel する
約1か月前
Bun ORM で bun:"m2m:join_table,join:A=B" の多対多リレーションを使う場合、join table のモデルを db.RegisterModel((*JoinTable)(nil)) で登録しておく必要がある。登録がない DB インスタンスで対象モデルを扱うと、クエリ実行前の model 初期化時点で bun: can't find m2m ... table (use db.RegisterModel) の panic が起きる。
適用条件
- Bun の
m2m:タグで多対多リレーションを定義している。 - アプリ本体以外にも、schema 生成、migration 生成、repository test、integration test、fixture 挿入 helper など、複数箇所で
bun.NewDBまたは DB 接続初期化を行っている。
判断基準
- m2m join table を追加したら、アプリ本体の DB 接続だけでなく、テスト用 DB helper、schema 生成用 DB 接続、sqlmock 用 DB 初期化など、全ての DB 初期化経路に同じ
RegisterModelを追加する。 - 1 箇所だけ直しても、別経路で同じモデルを insert/select した瞬間に panic するため、
bun.NewDBとRegisterModelの呼び出しを検索して漏れを確認する。 - 可能なら DB 初期化処理を共通化し、m2m 登録リストが本番・テスト・生成処理で分岐しないようにする。
検証
追加後は通常の unit test だけでなく、対象 repository の integration test や fixture 挿入 helper を通るテストを実行する。schema 生成処理が Bun の CreateTable を使っている場合は、schema/migration 生成コマンドも実行して、登録漏れで panic しないことを確認する。
JSONB カラムの内部型を変えたら既存データのエンコード形を backfill する
約1か月前
ORM model で JSONB カラムを汎用文字列として扱っていた実装から、配列や構造体などの厳格な内部型へ変える場合、コードだけを直しても既存データが読めなくなることがある。旧実装が配列を JSON 文字列として二重エンコードして保存していると、新実装の scan は「配列を期待したのに文字列が来た」状態になり、一覧取得や更新がサーバーエラーになる。
判断基準
- JSONB カラムの Go/TypeScript/ORM 側の型を
stringから[]Tや struct slice に変えるときは、既存 row の JSON 型も確認する。 - DB カラム型が
jsonbであっても、中身が array とは限らない。DB 側で JSON 型を確認し、array / object / string / null が想定と一致するか見る。 - 既存 row が JSON 文字列として保存されている場合は、コードデプロイだけでなく、JSON 文字列を取り出して JSON として戻す backfill が必要になる。
- その JSON に project や tenant などの文脈補完済みデータが含まれる場合、単純変換だけで正しいとは限らない。旧ソースや監査ログと照合して、補完バグで混入した値があれば同時に戻す。
検証
補正前に本文や機密値を出さず、対象カラムの JSON 型と件数を確認する。補正後は DB 側で対象カラムが期待する JSON 型に揃ったことを確認し、さらにアプリ/API 経由で一覧取得・更新対象の読み込みが成功することを確認する。