マイグレーション

スキーマ変更を SQL ファイルに記録して、各環境に順次適用する仕組み。 dev / deploy / push の使い分け、シャドウ DB、本番運用、破壊的変更まで。

マイグレーションフロー全体像

schema.prisma を編集
        │
        ▼
prisma migrate dev (開発)
   ├─ シャドウ DB で SQL 検証
   ├─ migrations/yyyymmddhhmmss_name/migration.sql 生成
   ├─ DB に適用
   └─ Client 再生成
        │
        ▼
git commit migrations/  + schema.prisma
        │
        ▼
本番デプロイ時
        ▼
prisma migrate deploy (本番)
   └─ 未適用の migration を順次実行
      

主要コマンド

migrate dev(開発専用)

npx prisma migrate dev --name add_user_role

migrate deploy(本番専用)

npx prisma migrate deploy

migrate reset(リセット)

npx prisma migrate reset

db push(プロトタイピング用)

npx prisma db push

migration ディレクトリの中身

prisma/
├── schema.prisma
└── migrations/
    ├── 20260101120000_init/
    │   └── migration.sql
    ├── 20260105140000_add_user_role/
    │   └── migration.sql
    └── migration_lock.toml      # provider 情報
      

migration.sql の例

-- CreateTable
CREATE TABLE "User" (
    "id" TEXT NOT NULL,
    "email" TEXT NOT NULL,
    "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,

    CONSTRAINT "User_pkey" PRIMARY KEY ("id")
);

-- CreateIndex
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");

シャドウ DB

migrate dev は schema 変更を検証するためにもう 1 つの DBを使う。

datasource db {
  provider          = "postgresql"
  url               = env("DATABASE_URL")
  shadowDatabaseUrl = env("SHADOW_DATABASE_URL")
}

migrate dev / deploy の正しい使い分け

環境コマンド
ローカル開発migrate dev
CI(PR)migrate diff で予習、migrate deploy でテスト用 DB に適用
ステージングmigrate deploy
本番migrate deploy

db push と migrate の使い分け

baseline(既存 DB を Prisma 管理に取り込む)

既存 DB を Prisma で管理し始めるとき、初期 migration を「すでに適用済み」とマークする手順:

# 1. 既存 DB から schema 取り込み
npx prisma db pull

# 2. 既存 DB の構造を SQL として保存
mkdir -p prisma/migrations/0_init
npx prisma migrate diff \
  --from-empty \
  --to-schema-datamodel prisma/schema.prisma \
  --script > prisma/migrations/0_init/migration.sql

# 3. 適用済みとマーク
npx prisma migrate resolve --applied 0_init

migration の手作業編集

生成された SQL を手で書き換えても OK。Prisma の DSL で表現できないこと(複雑な制約、トリガー、データ移行)はここで書く。

# SQL 生成だけして適用しない
npx prisma migrate dev --create-only --name add_search_index

# 編集してから
npx prisma migrate dev

例: 全文検索インデックスを足す

-- prisma/migrations/.../migration.sql に追記
CREATE INDEX "Post_fts_idx" ON "Post" USING GIN (to_tsvector('simple', "title"));

破壊的変更の戦略(expand & contract)

本番ではいきなり DROP しない。3 段階でゼロダウンタイム移行する:

  1. Expand: 新カラムを追加(新旧両方が動く状態)
  2. Migrate data: 旧 → 新 にデータコピー
  3. Contract: 旧カラムを削除(アプリ更新後)

例: fullNamefirstName + lastName に分割

-- ステップ 1: 新カラムを追加(NULL 許容)
ALTER TABLE "User" ADD COLUMN "firstName" TEXT;
ALTER TABLE "User" ADD COLUMN "lastName" TEXT;

-- ステップ 2: バックフィル(別 migration、別デプロイ)
UPDATE "User" SET
  "firstName" = SPLIT_PART("fullName", ' ', 1),
  "lastName" = SPLIT_PART("fullName", ' ', 2)
WHERE "firstName" IS NULL;

-- ステップ 3: NOT NULL + 旧カラム DROP(アプリ更新後の別 migration)
ALTER TABLE "User" ALTER COLUMN "firstName" SET NOT NULL;
ALTER TABLE "User" DROP COLUMN "fullName";

migration の差分プレビュー

npx prisma migrate diff \
  --from-schema-datamodel prisma/schema.prisma \
  --to-schema-datasource prisma/schema.prisma

現在の DB 状態と schema を比較して、差分 SQL を出力。CI で「予期しない変更」を検知するのに便利。

migration history が壊れたとき

resolve(マーク操作)

# 「適用済み」とマーク(手で SQL 流したあと)
npx prisma migrate resolve --applied "20260101_xxx"

# 「ロールバック済み」とマーク
npx prisma migrate resolve --rolled-back "20260101_xxx"

本番で壊れた場合

  1. 失敗した migration を手動で SQL 修正
  2. resolve --applied でマーク
  3. schema と migration ファイルを揃える
  4. その後の migrations は通常通り

seed

npx prisma db seed

migrate reset 時にも自動で実行される。 seed スクリプトは package.json で登録(セットアップ 参照)。

CI / CD パイプライン例(GitHub Actions)

name: deploy
on:
  push:
    branches: [main]

jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with: { node-version: 20 }
      - run: npm ci
      - run: npx prisma migrate deploy
        env:
          DATABASE_URL: ${{ secrets.DATABASE_URL }}
      - run: npx prisma generate
      - run: npm run build
      - run: ...                  # デプロイ

本番マイグレーションの安全 TIP

prisma db pull の使い所

migration の命名

--name は意味のある名前に。タイムスタンプは自動で付く。

失敗パターン

症状原因 / 対処
Drift detectedDB と migration history がズレた。migrate resolve で整合
shadow DB 失敗権限不足。SHADOW_DATABASE_URL 指定
本番で migrate dev 実行事故。即停止、migrate deploy
NOT NULL 追加失敗既存に NULL がある。デフォルト値 / 段階的移行
FK エラー関連テーブルの順序、CASCADE 設定
長時間ロックCONCURRENTLY、夜間メンテ、pg_repack
本番事故を避ける鉄則

1) ローカルでテスト / 2) ステージングで本番同等の data 量で時間計測 / 3) 本番デプロイ前にバックアップ / 4) ロックの可能性がある変更は低トラフィック時間帯に / 5) 失敗時のロールバック手順を事前に用意。