Help us understand the problem. What is going on with this article?

TypeORMで本番運用を見据えたマイグレーション

More than 1 year has passed since last update.

TypeScript で Node.js やる場合の O/R Mapper の選択肢は少ない。

メジャーっぽかったのと Nest.js で標準対応してるので TypeORM を使うことにしてみた。機能的には充分だが、本番環境での運用を想定した使いやすさには至ってないように感じた。

✍️ TypeORM の機能

😇 ここがダメだよ TypeORM

使いにくいと感じたところ。

CREATE TABLE をマイグレーションで管理しにくい

TypeORM では CLI ツールが提供されており、マイグレーションを管理する仕組みがある。

$ yarn global add typeorm
$ typeorm -v
0.2.7

CLI ツールで sync すると自分が定義した Entity クラスがすべて CREATE TABLE されて DB 反映される。

$ typeorm schema:sync

ただしこの実行内容は migrations テーブルで管理されないため、再実行するたびに ALTER TABLE が実行されてしまう。本番環境でこのコマンドはちょっと使えない。

マイグレーションファイル側で CREATE TABLE を記述することもできるが、それだとテーブル定義が Entity クラスとの二重管理になってしまうし、そもそも文法覚えるのもだるい。

ORM という性質上、テーブルの中身と Entity クラスを自動マッピングしてくれる役割なわけで、Entity クラスを作らないという選択肢はないしね。

CLI ツールで気軽に全テーブル削除できる

$ typeorm schema:drop

これだけで全テーブルが問答無用に削除される。明らかにテスト環境用のコマンドだけど、ターミナルのタブを別環境と間違えてしまい、うっかり本番環境で実行したりしそうで怖い。こういうのはデフォルトで --dry-run になってほしいが、DryRun のオプション自体がない。

🚑 本番運用するための対応策

マイグレーションについては CREATE TABLE 含めてマイグレーションファイルで管理したい。

少しトリッキーかもだが下記方法で実現できた。

sync はローカル環境だけで行う

sync すると CREATE 文がコンソールに出力されるのでコピーしておき、その内容をマイグレーションファイルにペーストする。

$ typeorm migration:create -n CreateUser
async up(queryRunner: QueryRunner): Promise<any> {
  await queryRunner.query('CREATE TABLE `users` (`id` int NOT NULL AUTO_INCREMENT, `created_at` datetime(0) NOT NULL DEFAULT NOW(), `updated_at` datetime(0) NOT NULL DEFAULT NOW(), PRIMARY KEY (`id`)) ENGINE=InnoDB');
}

async down(queryRunner: QueryRunner): Promise<any> { 
  await queryRunner.query('DROP TABLE IF EXISTS `typeorm_test`.`users`');
}

Entity クラスで synchronize: false にする

@Entity({name: 'users', synchronize: false})
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @CreateDateColumn({name: 'created_at', precision: 0, default: () => 'NOW()'})
  createdAt: Date;

  @UpdateDateColumn({name: 'updated_at', precision: 0, default: () => 'NOW()'})
  updatedAt: Date;
}

こうしておけば以後 sync コマンドの対象外となり、本番環境で schema:syncschema:drop を使う必要がなくなる。

マイグレーション実行は設定ファイルで migrationsRun: true にしておけばサーバ起動時に実行される。トランザクション使ってテーブル生成やテーブル変更が行われるので、難しいこと考えなくても新しいサーバをデプロイするだけでよくなる。

package.json

サーバ起動は yarn start するだけでよい。prestart から自動実行される。

"scripts": {
  "prestart": "rm -rf dist && tsc",
  "start": "node dist/main.js"
}

🗽 まとめ

TypeORM は必要な機能すべて揃ってはいるが、ホスピタリティというか開発者が便利につかえるというところまでは手が回ってないという感じ。

Issue も500件くらい溜まっていて、その半分はレスがついていない。需要と比べて開発者がかなり不足しているようだ。

cyberagent
サイバーエージェントは「21世紀を代表する会社を創る」をビジョンに掲げ、インターネットテレビ局「AbemaTV」の運営や国内トップシェアを誇るインターネット広告事業を展開しています。インターネット産業の変化に合わせ新規事業を生み出しながら事業拡大を続けています。
http://www.cyberagent.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした