マイグレーション
バージョン管理システム(例: Git)を使ってソースコードの変更を管理するのと同じように、マイグレーションを使ってデータベースへの変更を追跡できます。マイグレーションを使用すると、既存のデータベースを別の状態に移行したり、その逆に移行したりすることができます。これらの状態遷移は、新しい状態への移行方法と、古い状態に戻るために変更を元に戻す方法を記述したマイグレーションファイルに保存されます。
Sequelizeコマンドラインインターフェース(CLI)が必要です。CLIは、マイグレーションとプロジェクトのブートストラップをサポートしています。
Sequelizeにおけるマイグレーションは、2つの関数up
とdown
をエクスポートするjavascriptファイルであり、マイグレーションの実行方法と元に戻す方法を指示します。これらの関数は手動で定義しますが、手動で呼び出すことはありません。これらはCLIによって自動的に呼び出されます。これらの関数では、sequelize.query
やSequelizeが提供する他のメソッドを使用して、必要なクエリを実行するだけです。それ以上の特別な処理はありません。
CLIのインストール
Sequelize CLIをインストールするには
npm install --save-dev sequelize-cli
詳細については、CLI GitHubリポジトリを参照してください。
プロジェクトのブートストラップ
空のプロジェクトを作成するには、init
コマンドを実行する必要があります
npx sequelize-cli init
これにより、次のフォルダが作成されます
config
: CLIがデータベースに接続する方法を指示する設定ファイルが含まれていますmodels
: プロジェクトのすべてのモデルが含まれていますmigrations
: すべてのマイグレーションファイルが含まれていますseeders
: すべてのシードファイルが含まれています
設定
先に進む前に、CLIにデータベースへの接続方法を指示する必要があります。そのためには、デフォルトの設定ファイルconfig/config.json
を開きましょう。これは次のようなものです
{
"development": {
"username": "root",
"password": null,
"database": "database_development",
"host": "127.0.0.1",
"dialect": "mysql"
},
"test": {
"username": "root",
"password": null,
"database": "database_test",
"host": "127.0.0.1",
"dialect": "mysql"
},
"production": {
"username": "root",
"password": null,
"database": "database_production",
"host": "127.0.0.1",
"dialect": "mysql"
}
}
Sequelize CLIはデフォルトでmysqlを想定していることに注意してください。別のダイアレクトを使用している場合は、"dialect"
オプションの内容を変更する必要があります。
次に、このファイルを編集して、正しいデータベースの資格情報とダイアレクトを設定します。オブジェクトのキー(例: "development")は、model/index.js
でprocess.env.NODE_ENV
(未定義の場合は "development"がデフォルト値)を照合するために使用されます。
Sequelizeは、各ダイアレクトのデフォルトの接続ポートを使用します(たとえば、postgresの場合、ポート5432です)。別のポートを指定する必要がある場合は、"port"
フィールドを使用します(config/config.js
にはデフォルトでは存在しませんが、簡単に追加できます)。
注:データベースがまだ存在しない場合は、db:create
コマンドを呼び出すだけです。適切なアクセス権があれば、データベースが作成されます。
最初のモデル(とマイグレーション)の作成
CLIの設定ファイルが正しく設定されたら、最初のマイグレーションを作成する準備が整いました。簡単なコマンドを実行するだけです。
model:generate
コマンドを使用します。このコマンドには2つのオプションが必要です
name
:モデルの名前;attributes
:モデル属性のリスト。
User
という名前のモデルを作成しましょう。
npx sequelize-cli model:generate --name User --attributes firstName:string,lastName:string,email:string
これにより
models
フォルダーにモデルファイルuser
が作成されます。migrations
フォルダーにXXXXXXXXXXXXXX-create-user.js
のような名前のマイグレーションファイルが作成されます。
注:Sequelizeはモデルファイルのみを使用します。これはテーブルの表現です。一方、マイグレーションファイルは、CLIで使用される、そのモデル、より具体的にはそのテーブルの変更です。マイグレーションを、データベースの変更のコミットまたはログのように考えてください。
マイグレーションの実行
このステップまでは、データベースに何も挿入していません。最初のモデルUser
に必要なモデルとマイグレーションファイルを作成しただけです。データベースに実際にテーブルを作成するには、db:migrate
コマンドを実行する必要があります。
npx sequelize-cli db:migrate
このコマンドは、次のステップを実行します
- データベースに
SequelizeMeta
というテーブルがあることを確認します。このテーブルは、現在のデータベースでどのマイグレーションが実行されたかを記録するために使用されます - まだ実行されていないマイグレーションファイルを探し始めます。これは
SequelizeMeta
テーブルを確認することで可能です。この場合、前のステップで作成したXXXXXXXXXXXXXX-create-user.js
マイグレーションを実行します。 - マイグレーションファイルで指定されたすべての列を持つ
Users
というテーブルを作成します。
マイグレーションの取り消し
これで、テーブルが作成され、データベースに保存されました。マイグレーションを使用すると、コマンドを実行するだけで古い状態に戻すことができます。
db:migrate:undo
を使用できます。このコマンドは、最新のマイグレーションを元に戻します。
npx sequelize-cli db:migrate:undo
db:migrate:undo:all
コマンドを使用してすべてのマイグレーションを元に戻すことで、初期状態に戻すことができます。--to
オプションで名前を渡すことで、特定のマイグレーションに戻すこともできます。
npx sequelize-cli db:migrate:undo:all --to XXXXXXXXXXXXXX-create-posts.js
最初のシードの作成
デフォルトでいくつかのテーブルにデータを挿入したいとしましょう。前の例に続いて、User
テーブルのデモユーザーを作成することを検討できます。
すべてのデータマイグレーションを管理するには、シーダーを使用できます。シードファイルは、データベーステーブルにサンプルデータまたはテストデータを投入するために使用できる、データの変更です。
User
テーブルにデモユーザーを追加するシードファイルを作成しましょう。
npx sequelize-cli seed:generate --name demo-user
このコマンドは、seeders
フォルダーにシードファイルを作成します。ファイル名はXXXXXXXXXXXXXX-demo-user.js
のようになります。マイグレーションファイルと同じup/down
セマンティクスに従います。
次に、このファイルを編集して、User
テーブルにデモユーザーを挿入する必要があります。
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.bulkInsert('Users', [
{
firstName: 'John',
lastName: 'Doe',
email: '[email protected]',
createdAt: new Date(),
updatedAt: new Date(),
},
]);
},
down: (queryInterface, Sequelize) => {
return queryInterface.bulkDelete('Users', null, {});
},
};
シードの実行
前のステップでシードファイルを作成しましたが、データベースにコミットされていません。そのためには、簡単なコマンドを実行します。
npx sequelize-cli db:seed:all
これにより、シードファイルが実行され、デモユーザーがUser
テーブルに挿入されます。
注:シーダーの実行履歴は、マイグレーションとは異なり、SequelizeMeta
テーブルを使用しません。この動作を変更したい場合は、Storage
セクションをお読みください。
シードの取り消し
シーダーは、ストレージを使用している場合に取り消すことができます。これには2つのコマンドが使用できます
最新のシードを取り消す場合
npx sequelize-cli db:seed:undo
特定のシードを取り消す場合
npx sequelize-cli db:seed:undo --seed name-of-seed-as-in-data
すべてのシードを取り消す場合
npx sequelize-cli db:seed:undo:all
マイグレーションのスケルトン
次のスケルトンは、典型的なマイグレーションファイルを示しています。
module.exports = {
up: (queryInterface, Sequelize) => {
// logic for transforming into the new state
},
down: (queryInterface, Sequelize) => {
// logic for reverting the changes
},
};
migration:generate
を使用してこのファイルを生成できます。これにより、マイグレーションフォルダーにxxx-migration-skeleton.js
が作成されます。
npx sequelize-cli migration:generate --name migration-skeleton
渡されたqueryInterface
オブジェクトを使用して、データベースを変更できます。Sequelize
オブジェクトは、STRING
やINTEGER
などの使用可能なデータ型を格納します。関数up
またはdown
はPromise
を返す必要があります。例を見てみましょう
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Person', {
name: Sequelize.DataTypes.STRING,
isBetaMember: {
type: Sequelize.DataTypes.BOOLEAN,
defaultValue: false,
allowNull: false,
},
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Person');
},
};
次は、データベースで2つの変更を実行するマイグレーションの例です。自動的に管理されるトランザクションを使用して、すべての命令が正常に実行されるか、失敗した場合にロールバックされるようにします
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.sequelize.transaction(t => {
return Promise.all([
queryInterface.addColumn(
'Person',
'petName',
{
type: Sequelize.DataTypes.STRING,
},
{ transaction: t },
),
queryInterface.addColumn(
'Person',
'favoriteColor',
{
type: Sequelize.DataTypes.STRING,
},
{ transaction: t },
),
]);
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.sequelize.transaction(t => {
return Promise.all([
queryInterface.removeColumn('Person', 'petName', { transaction: t }),
queryInterface.removeColumn('Person', 'favoriteColor', {
transaction: t,
}),
]);
});
},
};
次の例は、外部キーを持つマイグレーションの例です。参照を使用して外部キーを指定できます
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('Person', {
name: Sequelize.DataTypes.STRING,
isBetaMember: {
type: Sequelize.DataTypes.BOOLEAN,
defaultValue: false,
allowNull: false,
},
userId: {
type: Sequelize.DataTypes.INTEGER,
references: {
model: {
tableName: 'users',
schema: 'schema',
},
key: 'id',
},
allowNull: false,
},
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Person');
},
};
次の例は、async/awaitを使用し、新しい列に一意のインデックスを、手動で管理されるトランザクションで作成するマイグレーションの例です
module.exports = {
async up(queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.addColumn(
'Person',
'petName',
{
type: Sequelize.DataTypes.STRING,
},
{ transaction },
);
await queryInterface.addIndex('Person', 'petName', {
fields: 'petName',
unique: true,
transaction,
});
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
async down(queryInterface, Sequelize) {
const transaction = await queryInterface.sequelize.transaction();
try {
await queryInterface.removeColumn('Person', 'petName', { transaction });
await transaction.commit();
} catch (err) {
await transaction.rollback();
throw err;
}
},
};
次の例は、条件付きで複数のフィールドで構成される一意のインデックスを作成するマイグレーションの例です。これにより、リレーションを複数回存在させることができますが、条件を満たすのは1つだけです
module.exports = {
up: (queryInterface, Sequelize) => {
queryInterface
.createTable('Person', {
name: Sequelize.DataTypes.STRING,
bool: {
type: Sequelize.DataTypes.BOOLEAN,
defaultValue: false,
},
})
.then((queryInterface, Sequelize) => {
queryInterface.addIndex('Person', ['name', 'bool'], {
indicesType: 'UNIQUE',
where: { bool: 'true' },
});
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('Person');
},
};
.sequelizerc
ファイル
これは特別な設定ファイルです。通常はCLIに引数として渡す以下のオプションを指定できます。
env
: コマンドを実行する環境config
: 設定ファイルへのパスoptions-path
: 追加オプションを含むJSONファイルへのパスmigrations-path
: マイグレーションフォルダへのパスseeders-path
: シーダーフォルダへのパスmodels-path
: モデルフォルダへのパスurl
: 使用するデータベース接続文字列。--configファイルを使用する代替手段debug
: 利用可能な場合、様々なデバッグ情報を表示
使用できるシナリオの例
migrations
、models
、seeders
、またはconfig
フォルダへのデフォルトパスを上書きしたい場合。config.json
をdatabase.json
のように別の名前に変更したい場合。
その他にも多くの場合に使用できます。このファイルをカスタム設定に使用する方法を見ていきましょう。
まず、プロジェクトのルートディレクトリに .sequelizerc
ファイルを作成し、以下の内容を記述します。
// .sequelizerc
const path = require('path');
module.exports = {
config: path.resolve('config', 'database.json'),
'models-path': path.resolve('db', 'models'),
'seeders-path': path.resolve('db', 'seeders'),
'migrations-path': path.resolve('db', 'migrations'),
};
この設定により、CLIに対して以下のことを指示します。
- 設定に
config/database.json
ファイルを使用する。 - モデルフォルダとして
db/models
を使用する。 - シーダーフォルダとして
db/seeders
を使用する。 - マイグレーションフォルダとして
db/migrations
を使用する。
動的な設定
設定ファイルはデフォルトでは config.json
という名前のJSONファイルです。しかし、環境変数にアクセスしたり、設定を決定するために他のコードを実行するなど、動的な設定が必要になる場合があります。
幸いなことに、Sequelize CLIは .json
ファイルと .js
ファイルの両方を読み込むことができます。これは .sequelizerc
ファイルで設定できます。エクスポートされたオブジェクトの config
オプションとして、 .js
ファイルへのパスを指定するだけです。
const path = require('path');
module.exports = {
config: path.resolve('config', 'config.js'),
};
これで、Sequelize CLIは設定オプションを取得するために config/config.js
をロードします。
config/config.js
ファイルの例
const fs = require('fs');
module.exports = {
development: {
username: 'database_dev',
password: 'database_dev',
database: 'database_dev',
host: '127.0.0.1',
port: 3306,
dialect: 'mysql',
dialectOptions: {
bigNumberStrings: true,
},
},
test: {
username: process.env.CI_DB_USERNAME,
password: process.env.CI_DB_PASSWORD,
database: process.env.CI_DB_NAME,
host: '127.0.0.1',
port: 3306,
dialect: 'mysql',
dialectOptions: {
bigNumberStrings: true,
},
},
production: {
username: process.env.PROD_DB_USERNAME,
password: process.env.PROD_DB_PASSWORD,
database: process.env.PROD_DB_NAME,
host: process.env.PROD_DB_HOSTNAME,
port: process.env.PROD_DB_PORT,
dialect: 'mysql',
dialectOptions: {
bigNumberStrings: true,
ssl: {
ca: fs.readFileSync(__dirname + '/mysql-ca-main.crt'),
},
},
},
};
上記の例では、カスタムダイアレクトオプションを設定に追加する方法も示しています。
Babelの使用
マイグレーションとシーダーでよりモダンな構文を有効にするには、babel-register
をインストールし、.sequelizerc
の先頭でそれを require するだけです。
npm i --save-dev babel-register
// .sequelizerc
require('babel-register');
const path = require('path');
module.exports = {
config: path.resolve('config', 'config.json'),
'models-path': path.resolve('models'),
'seeders-path': path.resolve('seeders'),
'migrations-path': path.resolve('migrations'),
};
もちろん、結果は(.babelrc
ファイルのような)Babelの設定に依存します。詳細については、babeljs.io を参照してください。
セキュリティに関するヒント
設定には環境変数を使用してください。パスワードなどの機密情報は、ソースコードの一部にすべきではなく(特にバージョン管理にコミットすべきではありません)。
ストレージ
使用できるストレージには、sequelize
、json
、none
の3種類があります。
sequelize
: マイグレーションとシーダーをSequelizeデータベースのテーブルに保存します。json
: マイグレーションとシーダーをjsonファイルに保存します。none
: マイグレーション/シーダーを保存しません。
マイグレーションストレージ
デフォルトでは、CLIは実行された各マイグレーションのエントリを含む SequelizeMeta
という名前のテーブルをデータベースに作成します。この動作を変更するには、設定ファイルに追加できる3つのオプションがあります。migrationStorage
を使用すると、マイグレーションに使用するストレージの種類を選択できます。json
を選択した場合、migrationStoragePath
を使用してファイルのパスを指定できます。指定しない場合、CLIは sequelize-meta.json
ファイルに書き込みます。データベースに情報を保持したいが、sequelize
を使用し、異なるテーブルを使用したい場合は、migrationStorageTableName
を使用してテーブル名を変更できます。また、migrationStorageTableSchema
プロパティを指定することで、SequelizeMeta
テーブルに異なるスキーマを定義できます。
{
"development": {
"username": "root",
"password": null,
"database": "database_development",
"host": "127.0.0.1",
"dialect": "mysql",
// Use a different storage type. Default: sequelize
"migrationStorage": "json",
// Use a different file name. Default: sequelize-meta.json
"migrationStoragePath": "sequelizeMeta.json",
// Use a different table name. Default: SequelizeMeta
"migrationStorageTableName": "sequelize_meta",
// Use a different schema for the SequelizeMeta table
"migrationStorageTableSchema": "custom_schema"
}
}
注意: none
ストレージはマイグレーションストレージとして推奨されません。使用する場合は、どのマイグレーションが実行されたか、または実行されなかったかの記録がないことの意味を理解してください。
シーダーストレージ
デフォルトでは、CLIは実行されたシードを保存しません。この動作を変更する場合(!)、設定ファイルで seederStorage
を使用してストレージタイプを変更できます。json
を選択した場合、seederStoragePath
を使用してファイルのパスを指定できます。指定しない場合、CLIは sequelize-data.json
ファイルに書き込みます。データベースに情報を保持したい場合、sequelize
を使用して、seederStorageTableName
を使用してテーブル名を指定できます。指定しない場合は、デフォルトで SequelizeData
になります。
{
"development": {
"username": "root",
"password": null,
"database": "database_development",
"host": "127.0.0.1",
"dialect": "mysql",
// Use a different storage. Default: none
"seederStorage": "json",
// Use a different file name. Default: sequelize-data.json
"seederStoragePath": "sequelizeData.json",
// Use a different table name. Default: SequelizeData
"seederStorageTableName": "sequelize_data"
}
}
設定接続文字列
データベースを定義する設定ファイルを使用する --config
オプションの代替として、--url
オプションを使用して接続文字列を渡すことができます。例:
npx sequelize-cli db:migrate --url 'mysql://root:password@mysql_host.com/database_name'
npmで package.json
スクリプトを使用する場合は、フラグを使用する際にコマンドに --
を追加するようにしてください。例:
// package.json
...
"scripts": {
"migrate:up": "npx sequelize-cli db:migrate",
"migrate:undo": "npx sequelize-cli db:migrate:undo"
},
...
コマンドは npm run migrate:up -- --url <url>
のように使用します。
プログラムによる使用
Sequelizeには、マイグレーションタスクの実行とロギングをプログラムで処理するためのumzugという姉妹ライブラリがあります。