msksgm’s blog

msksgm’s blog

Webエンジニアです.日々の勉強,読書,映画観賞,美術観賞の記録を載せます.勉強日記と読書日記は定期的に削除します.

typeormの設定をormconfig.tsから読み込む

typeorm の設定ファイルはデフォルトで,ormcofig.json ですが,このままだと以下の問題点があると考えます.

  • 環境変数を埋め込みづらい
  • プロジェクトのルートディレクトリにファイルが増える
  • 自動で読み込まれるのでデバックしづらい

環境変数を読み込むには ts ファイルの方が楽ですし,他の config とまとめることができるので,ormconfig.ts から読み込む方法を記述します.

環境構築の方法は,こちらの記事の"node.js 14.17.0 をインストール"までを参照してください.

完成したソースコードこちら

環境構築

typeorm init

ディレクトリを作成

mkdir typeorm_ormcofig.ts_example
cd typeorm_ormcofig.ts_example

typerom initで typeorm の構成を自動生成.(typerom を使う時に必須ではないですが,簡略化のため使用しました.)
DB は MySQL を使用

npx typeorm init --database mysql

生成されるファイル構成は以下(node_modules は除外)

tree -I node_modules
.
├── README.md
├── ormconfig.json
├── package-lock.json
├── package.json
├── src
│   ├── entity
│   │   └── User.ts
│   ├── index.ts
│   └── migration
└── tsconfig.json

3 directories, 7 files

MySQL サーバー をコンテナで作成

docker compose

Docker で,MySQL サーバーを作成します.

プロジェクトのルートディレクトリにdocker-compose.yml を作成.

version: "3.5"
services:
  db:
    image: mysql:5.7.34
    container_name: mysql-container
    environment:
      MYSQL_DATABASE: "typeorm_db"
      MYSQL_ROOT_PASSWORD: "password"
    restart: "always"
    ports:
      - "13306:3306"

以下の箇所はホストのポート:コンテナのポートなので,13306 は任意の値で良い.

ports:
  - "13306:3306"

MySQL へ接続を確認

コンテナを起動

docker compose up -d

接続確認.パスワードはdocker-compose.ymlに記述したpasswordtypeorm_db
ログインできたら成功

mysql -u root --port 13306 -h 127.0.0.1 -D typeorm_db -p

.env ファイルを作成

プロジェクトのルートディレクトリに.envファイルを作成.

.env

# DB
DB_HOST=127.0.0.1
DB_PORT=13306
DB_USERNAME=root
DB_PASSWORD=password
DB_DATABASE=typeorm_db

.env ファイルは.gitignore に追加しましょう

.idea/
.vscode/
node_modules/
build/
tmp/
temp/
# 追加
.env

以下のコマンドで読み込む

export $(cat .env | grep -v ^# | xargs)

実装

ormconfig.ts を作成

src/configディレクトリを作成し,src/config/ormconfig.tsファイルを作成する.

記述内容は以下.

/** 全て追加 **/
import { ConnectionOptions } from "typeorm";

const ormconfig: ConnectionOptions = {
  type: "mysql",
  host: process.env.DB_HOST,
  port: parseInt(process.env.DB_PORT, 10),
  username: process.env.DB_USERNAME,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_DATABASE,
  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",
  },
};

export default ormconfig;

ormconfig.jsonは不要になったので削除しましょう.

rm ormconfig.json

ormconfig.ts を読み込む

src/index.tsを以下のように修正する.

import "reflect-metadata";
import { createConnection } from "typeorm";
import { User } from "./entity/User";
import connectionOption from "./config/ormconfig"; // 追加

createConnection(connectionOption) // 修正
  .then(async (connection) => {
    console.log("Inserting a new user into the database...");
    const user = new User();
    user.firstName = "Timber";
    user.lastName = "Saw";
    user.age = 25;
    await connection.manager.save(user);
    console.log("Saved a new user with id: " + user.id);

    console.log("Loading users from the database...");
    const users = await connection.manager.find(User);
    console.log("Loaded users: ", users);

    console.log("Here you can setup and run express/koa/any other framework.");
  })
  .catch((error) => console.log(error));

動作確認

MySQL でデータベースにはテーブルが存在しないことを確認

以下のコマンドで何も表示されないことを確認する

mysql -u root --port 13306 -h 127.0.0.1 -D typeorm_db -e 'SHOW TABLES;' -p

migration ファイルを生成

migration:generateで migration ファイル が生成される.
-f で ormconfig.ts へのパスを書くのがポイント.デフォルトではルートにある ormconfig.json を取得しようとする.

npx ts-node ./node_modules/.bin/typeorm migration:generate -n user -f ./src/config/ormconfig.ts

以下のような表示がされたら成功.

Migration /PATH/TO/typeorm_ormcofig.ts_example/src/migration/1623800662217-user.ts has been generated successfully.

migration 実行

migration:runで migration を実行することができる.
以下のコマンドに typeorm_db データベースに user テーブルが追加されます.

npx ts-node ./node_modules/.bin/typeorm migration:run -f ./src/config/ormconfig.ts
query: SELECT * FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = 'typeorm_db' AND `TABLE_NAME` = 'migrations'
query: CREATE TABLE `typeorm_db`.`migrations` (`id` int NOT NULL AUTO_INCREMENT, `timestamp` bigint NOT NULL, `name` varchar(255) NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB
query: SELECT * FROM `typeorm_db`.`migrations` `migrations` ORDER BY `id` DESC
0 migrations are already loaded in the database.
1 migrations were found in the source code.
1 migrations are new migrations that needs to be executed.
query: START TRANSACTION
query: CREATE TABLE `user` (`id` int NOT NULL AUTO_INCREMENT, `firstName` varchar(255) NOT NULL, `lastName` varchar(255) NOT NULL, `age` int NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB
query: INSERT INTO `typeorm_db`.`migrations`(`timestamp`, `name`) VALUES (?, ?) -- PARAMETERS: [1623800662217,"user1623800662217"]
Migration user1623800662217 has been executed successfully.
query: COMMIT

MySQL でテーブルを確認

さきほどと同じコマンドを実行して,テーブルが追加されていることを確認する.

mysql -u root --port 13306 -h 127.0.0.1 -D typeorm_db -e 'show tables;' -p
+----------------------+
| Tables_in_typeorm_db |
+----------------------+
| migrations           |
| user                 |
+----------------------+

また,以下のコマンドで user テーブルにはカラムがないことがわかります.実行してもなにも表示されません.

mysql -u root --port 13306 -h 127.0.0.1 -D typeorm_db -e 'SELECT * FROM user;' -p

user テーブルにカラムを追加.

typeorm initでは,src/index.tsにデータベースにカラムを追加する処理が含まれているので実行します,
実行後,ctrl + cで停止しても大丈夫です.

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.

MySQL でカラムを確認

先ほどと同様にカラムを確認します.
カラムが追加されたことがわかります.

mysql -u root --port 13306 -h 127.0.0.1 -D typeorm_db -e 'SELECT * FROM user;' -p

出力

+----+-----------+----------+-----+
| id | firstName | lastName | age |
+----+-----------+----------+-----+
|  1 | Timber    | Saw      |  25 |
+----+-----------+----------+-----+

まとめ

typeorm の設定ファイルを ormconfig.json から,ormconfig.ts に変更する方法と動作確認について執筆しました.
正直,一般的に使われる手法がどちらなのかはわからないので,設計を考えるときや,一緒に作業する人と相談などして決めましょう.
また今回は,src/config/ormconfig.tsにまとめましたが,src/config/index.tsを作成して,他の設定とまとめても良いと思います.

備考

DB 関連

Q1. MySQL に接続するコマンドを実行すると,以下のエラーがでる

ERROR 2013 (HY000): Lost connection to MySQL server at 'reading initial communication packet', system error: 0

A1. DB サーバーが起動できていないです.もう少し待ったら動くようになります.

Q2. DB を永続化したい.

A2. volume の設定を追加する.(以下は,一例)

version: "3.5"
services:
  db:
    image: mysql:5.7.34
    container_name: mysql-container
    environment:
      MYSQL_DATABASE: "typeorm_db"
      MYSQL_ROOT_PASSWORD: "password"
    restart: "always"
    ports:
      - "13306:3306"
    volumes:
      - ./.data/db:/var/lib/mysql

Q3. DB に接続するときに,なぜ127.0.0.1が必要なの?

A3. 何も指定しないと,localhost:3306に接続されてしまう現象を確認しています.自分も詳細は理解していませんが,こちらの記事が参考になりました.

typescript 関連

Q1. 以下の"mysql"の箇所も環境変数にしたい.

/** 全て追加 **/
import { ConnectionOptions } from "typeorm";

const ormconfig: ConnectionOptions = {
  type: "mysql",
  host: process.env.DB_HOST,
  port: parseInt(process.env.DB_PORT, 10),
  username: process.env.DB_USERNAME,
  password: process.env.DB_PASSWORD,
  database: process.env.DB_DATABASE,
  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",
  },
};

export default ormconfig;

A1. 型チェックで怒られて対処できていないです...as なんとかでできそうですが,どなたか教えていただきたいです.