📘

TypeORMを使って、Express + Docker + MySQLの環境を構築する

2021/10/28に公開1

TypeORMとは、Typescript用のORマッパーです。TypeORMを使って、Express + Docker + MySQLの環境構築からマイグレーションまでを行います。

OSはMacを使います。Node、Yarn、Dockerが既にインストールされていることを前提に進めます。扱うツールなどのバージョンは以下の通りです。

Node Yarn Express TypeScript MySQL
16.4.2 1.22.11 4.17.1 4.4.3 5.7.10

環境構築

プロジェクトのフォルダを作成します。

❯ mkdir typeorm-sample
❯ cd typeorm-sample

TypeORMをインストールします。

❯ yarn add typeorm

NodeでTypeScriptを使いたいため、typescriptとts-nodeをインストールします。

❯ yarn add -D typescript ts-node

typeorm initコマンドで、TypeORMの利用に必要なファイルを生成します。更にMySQLとExpress、Dockerも入れます。

❯ yarn typeorm init --database mysql --express --docker
Project created inside current directory.

ファイルを確認すると、docker-compose.ymlが生成されています。docker-compose.ymlの中身を見るとmysql 5.7のimageが設定されているので、そのまま使います。

❯ ls
README.md          ormconfig.json     src
docker-compose.yml package-lock.json  tsconfig.json
node_modules       package.json       yarn.lock

起動

Dockerを立ち上げて、MySQLを使えるようにします。

❯ docker compose up

docker-compose.ymlに書かれている情報を確認して、MySQLに接続します。

❯ docker compose exec mysql /bin/bash
❯ mysql -utest -ptest
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| test               |
+--------------------+
2 rows in set (0.00 sec)

接続が確認できました。
続いてサーバーを起動します。

❯ yarn
❯ yarn start
yarn run v1.22.11
warning package.json: No license field
$ ts-node src/index.ts
Express server has started on port 3000. Open http://localhost:3000/users to see results

http://localhost:3000/usersにアクセスすると、以下のJSONが画面に表示されます。

[{"id":1,"firstName":"Timber","lastName":"Saw","age":27},{"id":2,"firstName":"Phantom","lastName":"Assassin","age":24}]

これはyarn typeorm initした際に、デフォルトでuserテーブルとそれに関連するデータが作成されたためです。

mysql> show columns from user;
+-----------+--------------+------+-----+---------+----------------+
| Field     | Type         | Null | Key | Default | Extra          |
+-----------+--------------+------+-----+---------+----------------+
| id        | int(11)      | NO   | PRI | NULL    | auto_increment |
| firstName | varchar(255) | NO   |     | NULL    |                |
| lastName  | varchar(255) | NO   |     | NULL    |                |
| age       | int(11)      | NO   |     | NULL    |                |
+-----------+--------------+------+-----+---------+----------------+
4 rows in set (0.01 sec)

テーブルの作成

次はTypeORMのマイグレーション機能を使って、テーブルを作成します。

Entityの作成

最初にEntityを定義します。Entityとはテーブルの構造をクラス構文で表現したものです。今回はTODOリストを題材に必要なカラムを用意します。

TODOを保存するための、テーブルの定義は以下にします。

テーブル名:task

カラム論理名 カラム物理名
ID task_id integer
タイトル title varchar
期日 due_date date
状態 status tinyint
作成日 created_at datetime
更新日 updated_at datetime

これを元にEntityを書いていきます。

src/entity/Task.ts
import {
  Entity,
  Column,
  PrimaryGeneratedColumn,
  CreateDateColumn,
  UpdateDateColumn,
} from 'typeorm';

@Entity()
export class Task {
  @PrimaryGeneratedColumn()
  readonly task_id: number;

  @Column('varchar', { length: 20, nullable: false })
  title: string;

  @Column('date', { nullable: false })
  due_date: Date;

  @Column('tinyint', { width: 1, default: 1 })
  status: number;

  @CreateDateColumn()
  readonly created_at?: Date;

  @UpdateDateColumn()
  readonly updated_at?: Date;
}

Entityの書き方は、以下が参考になります。
https://github.com/typeorm/typeorm/blob/master/docs/entities.md

マイグレーションファイルの作成

マイグレーションファイルを作成します。
-nは必須のパラメータで、ファイル名を指定します。

❯ yarn ts-node node_modules/.bin/typeorm migration:generate -n Task

実行後は、src/migration/1635378767371-Task.tsのようにファイルが作成されます。1635378767371の部分は、タイムスタンプです。

src/migration/1635378767371-Task.ts
import {MigrationInterface, QueryRunner} from "typeorm";

export class Task1635378767371 implements MigrationInterface {
    name = 'Task1635378767371'

    public async up(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`CREATE TABLE \`task\` (\`task_id\` int NOT NULL AUTO_INCREMENT, \`title\` varchar(20) NOT NULL, \`due_date\` date NOT NULL, \`status\` tinyint(1) NOT NULL DEFAULT '1', \`created_at\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), \`updated_at\` datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6) ON UPDATE CURRENT_TIMESTAMP(6), PRIMARY KEY (\`task_id\`)) ENGINE=InnoDB`);
    }

    public async down(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`DROP TABLE \`task\``);
    }

}
  • upメソッド:実際にマイグレーションの際に実行されるクエリです
  • downメソッド:マイグレーションのロールバックの際に利用されるクエリです

マイグレーションの実行

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

反映されているマイグレーションを確認します。

❯ yarn ts-node node_modules/.bin/typeorm migration:show

[X] Task1635378767371
✨  Done in 1.53s.

もし、1つ前のマイグレーションをロールバックしたい場合は、以下のコマンドを実行します。

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

念のため、MySQLに接続して、taskテーブルのカラム一覧を確認した所、問題なく作成されていました。

mysql> show columns from task\G
*************************** 1. row ***************************
  Field: task_id
   Type: int(11)
   Null: NO
    Key: PRI
Default: NULL
  Extra: auto_increment
*************************** 2. row ***************************
  Field: title
   Type: varchar(20)
   Null: NO
    Key: 
Default: NULL
  Extra: 
*************************** 3. row ***************************
  Field: due_date
   Type: date
   Null: NO
    Key: 
Default: NULL
  Extra: 
*************************** 4. row ***************************
  Field: status
   Type: tinyint(1)
   Null: NO
    Key: 
Default: 1
  Extra: 
*************************** 5. row ***************************
  Field: created_at
   Type: datetime(6)
   Null: NO
    Key: 
Default: CURRENT_TIMESTAMP(6)
  Extra: 
*************************** 6. row ***************************
  Field: updated_at
   Type: datetime(6)
   Null: NO
    Key: 
Default: CURRENT_TIMESTAMP(6)
  Extra: on update CURRENT_TIMESTAMP(6)
6 rows in set (0.00 sec)

以上です。

参考

https://typeorm.io/#/

Discussion

菊谷 知真菊谷 知真

現在のTypeORMのバージョンではこの手順でマイグレーションが動作しませんでした