あいつの日誌β

働きながら旅しています。

TypeORM で本番環境にデプロイしたら自動的に migration させる方法

あらすじ

typeORM で新規サービスを始めるときに、そういえば TypeORM の migration ってどうするんだっけ?ってなったので調べました。

環境

mysql --version
mysql  Ver 8.0.18 for osx10.15 on x86_64 (Homebrew)

% node -v
v12.13.0

% typeorm -v
0.2.22

雛形を作成

mkdir practice-typeorm-migration && cd $_
typeorm init --database mysql

この時点ではまだ node_modules に必要なモジュールがインストールされていないのでインストールします

yarn install

既にDBに接続する方法が ormconfig.json に記述されているのでそれを活用します。mysql に接続用のユーザー test が存在しない場合は以下のようにして作成してください。

mysql> create user 'test'@'localhost' IDENTIFIED WITH password BY 'test';
mysql> grant all on *.* to 'test'@'localhost' with grant option;
mysql> flush privileges;

なお、Error: ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server; consider upgrading MySQL client というエラーメッセージが表示された場合は MySQL 8.0からパスワードの認証形式が変更され、ライブラリ側で互換対応がされていない ため、以下のように古いパスワード認証を利用します。

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';

typeorm で本番運用を考える

おそらく以下のようにするのが無難だと思われるのでこの作法を模倣する

https://qiita.com/jnst/items/9a4c1a9f15b165e0e420 https://medium.com/better-programming/typeorm-migrations-explained-fdb4f27cb1b3

ormconfig の設定で synchronize: false にする

以下のように ormconfig.json を修正します。

{
   "type": "mysql",
   "host": "localhost",
   "port": 3306,
   "username": "test",
   "password": "test",
   "database": "test",
+   "synchronize": false,
-   "synchronize": true,
   "logging": false,
   "entities": [
      "src/entity/**/*.ts"
   ],
   "migrations": [
      "src/migration/**/*.ts"
   ],
   "subscribers": [
      "src/subscriber/**/*.ts"
   ],
   "cli": {
      "entitiesDir": "src/entity",
      "migrationsDir": "src/migration",
      "subscribersDir": "src/subscriber"
   }
}

この状態で ts-node src/index.ts を実行すると users テーブルがまだ生成されていないためエラーが発生します。Entity と synchronize させていると開発初期には便利ですが、運用を考えると synchronize は false の方が良いと思います。

続いてこの Entity の migration ファイルを作成します。

% ts-node ./node_modules/.bin/typeorm migration:generate -n InitialSchema

migration を実行します。

ts-node ./node_modules/.bin/typeorm migration:run

もう一度以下のコマンドが実行して動作することを確認してください。

ts-node src/index.ts

ormconfig の設定で migrationsRun: true にする

続いて ormconfig.json に migrationsRun を追加します。

{
   "type": "mysql",
   "host": "localhost",
   "port": 3306,
   "username": "test",
   "password": "test",
   "database": "test",
   "synchronize": false,
+   "migrationsRun": true,
   "logging": false,
   "entities": [
      "src/entity/**/*.ts"
   ],
   "migrations": [
      "src/migration/**/*.ts"
   ],
   "subscribers": [
      "src/subscriber/**/*.ts"
   ],
   "cli": {
      "entitiesDir": "src/entity",
      "migrationsDir": "src/migration",
      "subscribersDir": "src/subscriber"
   }
}

この設定で node-ts src/index.ts を実行してみます。migration が全て実行されている状態だと思いますので特に DB に変化は起きないと思います。一度 revert してみます。

ts-node ./node_modules/.bin/typeorm migration:revert

revert が成功したら user TABLE が削除されていると思いますがもう一度以下のコマンドを実行してください。Node プロセスが起動するたびに DB への migration が行われるようになりました。

% ts-node src/index.ts
Inserting a new user into the database...
Saved a new user with id: 1
Loading users from the database...
Loaded users:  [ User { id: 1, firstName: 'Timber', lastName: 'Saw', age: 25 } ]
Here you can setup and run express/koa/any other framework.

まとめ

TypeORM が使われているプロジェクトで本番環境での migration をどのようにするべきかを考察しました。DB schema に変更があった場合、ソースコードからプロセスを起動した瞬間に migration が実行されるので本番へのデプロイが容易に行えると思います。

開発環境と本願環境で ormconfig.json を切り替えたり、そういった作法も必要なのかもしれませんが、それらに関する考察はきっと後世のエンジニアがもっと良い記事を書いてくれると思います。

こちらからは以上です。