マイグレーションは、固定のスキーマや過去のデータの変更を記録します。アプリとプラグインは、同じ defineMigration() の API を使います。ファイルの場所と組み込みについては、テーブルを作成してデータを初期化する と プラグインのデータベースのリソース を参照してください。
マイグレーションを定義する
既存の orders コレクションにメモを追加するこのマイグレーションを、202611040010_add_order_note.ts として保存します。
import { defineMigration } from '@nocobase/db';
export default defineMigration({
name: '202611040010_add_order_note',
async up({ builder }) {
await builder.alterCollection('orders', (c) => {
c.text('note').nullable();
});
},
async down({ builder }) {
await builder.dropField('orders', 'note');
},
});defineMigration() は定義に印を付けて返すだけで、実行はしません。ローダーは、その戻り値をデフォルトエクスポートとし、name が拡張子を除いたファイル名と一致することを求めます。名前付きの up/down を別々にエクスポートしたものや、プレーンなオブジェクトは受け付けません。
| オプション | 役割 |
|---|---|
name | その接続のマイグレーションの取得元の中で一意。順序を決める |
up(context) | 変更の必須の実装 |
down(context) | 逆の操作。フィールドやテーブルを削除すると、そのデータも失われる |
irreversible | 確実に元に戻す方法がない場合は true にする。down とは共存できない |
transaction | true、false、または既定の 'auto' |
shouldRun(context) | 保留中のマイグレーションの実行前に、適用するかどうかを判定する真偽値のチェック |
down() か irreversible: true のどちらかを用意します。変化していくモデル、サービス、共有のスキーマの定義をインポートするのではなく、決まったフィールド、インデックス、制約で、マイグレーションを自己完結に保ちます。
マイグレーションのコンテキストを使う
up()、down()、shouldRun() は MigrationContext を受け取ります。タスクに必要なメンバーを分割代入します。
| メンバー | 役割と境界 |
|---|---|
builder | スキーマとモデルのメタデータの変更。テーブルとフィールド を参照 |
query | 行の読み取りと集合単位の修正。通常のマイグレーションでのデータのツール |
repository(name) | コレクションのコーデック、コレクションの命名の上書き、リレーションの書き込み |
connection | 接続の name、driver、dialect、capabilities と、低レベルの client() |
config | get() による読み取り専用の設定 |
container | タスクの所有者が提供する、制限されたサービスのリゾルバー |
configuration、parameters | 取得元からの、任意の適用条件の設定と固定の対象のパラメーター |
connection は完全な実行時の接続ではありません。repository()、builder、transaction()、トランザクションのコールバックはありません。ランナーがタスクの接続とトランザクションに結び付けられるよう、コンテキストのトップレベルのツールを使います。外部の DatabaseManager から Repository を取得すると、そのトランザクションの外で書き込むことがあります。
マイグレーションの repository() は、実行時点のモデルを解決します。Query では正しく表現できない操作にだけ使い、その理由をコードのコメントで説明してください。down() でそれに依存したり、テーブル全体を Repository で 1 行ずつ読んで更新したりしないでください。マイグレーションとシードでのコンテキストの Repository の書き込みは、実行時の Repository の変更のイベントを発行しません。インストールが、業務のリスナーによるレコードの補完に依存してはいけません。
過去のデータを埋め戻して変換する
古い値は新しいマイグレーションで修正します。これは古い綴りだけを変え、ほかのステータスは保ちます。
import { defineMigration } from '@nocobase/db';
export default defineMigration({
name: '202611040011_normalize_order_status',
irreversible: true,
async up({ query }) {
await query.updateTable('orders')
.set({ status: 'draft' })
.where('status', '=', 'DRAFT')
.execute();
},
});実行後、DRAFT は draft になり、ほかのレコードは変わりません。すでに draft だったレコードと変換されたレコードは区別できないため、この例は明示的に元に戻せないものとしています。元に戻す必要がある場合は、反対の一括更新で元に戻せるふりをするのではなく、変更の設計時に元のデータを十分に残しておきます。
Query は接続の命名を使いますが、コレクション固有のコーデックやリレーションの書き込みは使いません。通常の条件付きの更新には Query を、それらの意味が必要な場合にだけコンテキストの Repository を使います。updateTable() と deleteFrom() には条件が必要です。意図したテーブル全体の操作にだけ、明示的な allowAllRows() を使います。
新しい必須のフィールドでは、まず NULL 許容で追加し、明示的なルールで既存のレコードを埋め戻し、結果を確認してから NULL 不可にします。空のデータベースには変換すべき過去の行がありませんが、同じスキーマに到達すべきです。同じ実行の中でまだ実行されていないシードに依存しないでください。
設定と許可されたサービスを読み取る
config.get<T>(key) はランナーが提供した設定を読み取り、キーがない場合は undefined を返します。ジェネリクスは実行時に検証しません。たとえば、設定された初期管理者の名前を読み取ります。
const username = config.get<string>('users.initialAdmin.username');アプリの起動とデータベースのコマンドは設定のスナップショットを提供します。単独のデータベースのランナーは、設定しない限り空のリーダーを既定にします。設定はマイグレーションの識別情報の一部ではなく、ロールバックはそのあとの時点で利用できる設定を受け取ります。変更可能な設定からスキーマやその逆の操作を導かないでください。
アプリレベルの container が現在許可しているのは、新しい識別子が必要なデータ操作のための idGeneratorToken だけです。
import { idGeneratorToken } from '@nocobase/app-server/id-generator';
// Inside a callback that receives container:
const id = container.resolve(idGeneratorToken).generateString();これはアプリの完全なコンテナではなく、DatabaseManager や任意の業務サービスは解決できません。単独の呼び出し側は、必要なサービスを提供する必要があります。生成した ID は、シードが既存のレコードを認識するための固定の業務キーの代わりにもなりません。
条件付きの実行
任意のコレクションにだけ適用するマイグレーションには shouldRun() を使います。これは先ほどのステータスの修正の、任意のコレクション向けの版です。アプリに合った版を選んでください。
import { defineMigration } from '@nocobase/db';
export default defineMigration({
name: '202611040012_normalize_optional_order_status',
irreversible: true,
async shouldRun({ builder }) {
return builder.hasCollection('orders');
},
async up({ query }) {
await query.updateTable('orders')
.set({ status: 'draft' })
.where('status', '=', 'DRAFT')
.execute();
},
});条件は、タスクのトランザクションが始まる前に、マイグレーションのロックの下で実行され、状態を確認するだけにすべきです。false を返すと成功の履歴は記録されず、あとの実行で再び確認されます。true を返すと up() を実行できます。例外を投げたり、真偽値以外を返したりすると、実行は失敗します。一度成功すると、条件を再確認せずに履歴によってスキップされます。
必須のスキーマがないことを隠すために条件を使わないでください。db apply --dry-run は条件を呼び出さないため、プレビューに表示されたマイグレーションが実行されないこともあります。
取得元の parameters は、同じマイグレーションの取得元を異なる固定の対象に適用する場合にだけ使います。パラメーターのセットが異なれば履歴の識別情報も異なります。configuration は識別情報に影響しません。通常のアプリやプラグインのマイグレーションには、どちらも必要ありません。インターフェースについては MigrationSource を参照してください。
実行順序、履歴、トランザクション
1 つの接続のアプリとプラグインのマイグレーションはまとめられ、名前の重複を確認されて、名前の順に並べられます。プラグインの登録順はマイグレーションの順序ではありません。実行されるのは成功の履歴がないマイグレーションだけで、1 回の実行で適用されたものは新しいバッチ番号を共有します。各タスクはそれぞれ独自のトランザクションを持ち、バッチ全体で 1 つのトランザクションではありません。
現在、transaction: 'auto' と true はどちらも接続のトランザクションに入り、up() とその成功の履歴の書き込みはその中で行われます。false ではタスクのトランザクションは作られず、失敗すると成功の記録なしに作業の一部が残ることがあります。トランザクションを有効にしても、すべてのダイアレクトが DDL をロールバックできるわけではありません。
失敗すると実行は止まり、それより前に完了したマイグレーションは残ります。あとの実行ではそれらをスキップし、未完了の作業を再試行します。マイグレーションのロックは、同じロックのプロトコルを使うランナーを直列化しますが、接続をまたいだアトミック性も、業務上の一意性も提供しません。接続の選択とフラグについては CLI リファレンス を参照してください。
ロールバックと元に戻せない変更
db rollback は最新のバッチ全体を扱い、実行と逆の順序で down() を呼び出して、そのマイグレーションの履歴を削除します。バッチにはアプリとプラグインのマイグレーションが含まれることがあります。ランナーは開始前にバッチ全体を検証し、元に戻せないマイグレーションが 1 つでもあると、ロールバックは始まりません。
ロールバックしてもかまわない分離したデータベースでプレビューします。
pnpm nocobase db rollback --dry-run範囲を確認したら、--dry-run なしで実行します。対話型のターミナルでは確認を求められ、非対話型の実行では明示的な --force が必要です。ロールバックも、1 つのアトミックなバッチのトランザクションではなく、タスクごとのトランザクションを使います。
シードには down() がなく、マイグレーションのロールバックでもシードの履歴はクリアされません。db redo はロールバックしてから保留中のマイグレーションとシードを適用するため、テーブルを作り直しても、成功したシードがそのレコードを再インストールするとは限りません。db reset は、down() を呼び出さずに管理対象のスキーマと履歴を削除し、空の状態からインストールします。意図して捨ててよいデータにだけ使い、アップグレードのテストやバックアップの復元には使わないでください。
チェックサムとコンパイル済みの成果物
マージまたはリリースしたら、マイグレーションの内容、名前、ファイル名は保ちます。フォーマットやコメントの編集でもチェックサムは変わります。その後の修正には、あとの順序になるマイグレーションを追加します。
実行済みのファイルが変わると、既定では警告が出ます。onChecksumMismatch: 'error' にすると実行を拒否できます。db repair は記録されたチェックサムを現在のファイルに合わせるだけです。マイグレーションを実行したり、スキーマを修正したり、タスクを再び保留中にしたりはしません。データベースがすでに現在のソースの意味と一致していることを確認してから、検討してください。マージ前のマイグレーションの開発中は db redo を使います。リリース済みの変更には、引き続き新しいマイグレーションが必要です。
標準のビルドでは、マイグレーションとシードをコンパイルしたあとに .manifest.json が生成され、出力された成果物を検証しながらソースのチェックサムを保ちます。既存のビルドの手順とマニフェストは残してください。成果物がない場合や一致しない場合は、履歴やマニフェストを手で編集するのではなく、ビルドし直します。プラグインについては データベースのリソースをビルドして引き渡す を参照してください。
マイグレーションを検証する
アプリとプラグインは @nocobase/app-testing/server から describeMigration() をインポートし、実際のマイグレーションの取得元を渡します。before で過去のデータを用意し、up で変更されたスキーマとレコードを確認し、down で元に戻したあとのデータを確認します。元に戻せないマイグレーションには reversible: false を設定します。
このヘルパーは、物理的なスキーマとメタデータの整合性と、元に戻せるマイグレーションのロールバックと再適用を確認します。業務上のアサーション、特に既存のレコードの保持、埋め戻しの条件、無効な参照、削除の影響は、自分で書いてください。データベースのテスト を参照してください。