TypeORM でハマったところを書いておく


ormconfig は .json ではなく .js または .ts にしたほうが便利

デフォルトだと ormconfig.json ファイルが生成されるけど、ファイルフォーマットは選べる。
https://typeorm.io/#/using-ormconfig/creating-a-new-connection-from-the-configuration-file

で、ormconfig にデータベースの接続情報を書くけど、直書きしたくはないので、.jsとしておくと
以下のように環境変数を読めるので、便利!

ormconfig.js
module.exports = {
  'type': 'mysql',
  'host': process.env['MYSQL_HOST'] || 'localhost',
  'port': process.env['MYSQL_PORT'] || 3306,
  'username': process.env['MYSQL_USER'] || 'username',
  'password': process.env['MYSQL_PASSWORD'] || 'password',
  'database': process.env['MYSQL_DATABASE'] || 'database',
  ...
};

synchronize オプションは false (デフォルト false なので設定しないでよい)

ドキュメントにある通り、synchronize オプションを true にすると、Entity の設定がデータベースに反映されちゃう。
Entity にリレーション設定をしてると、外部キーカラムが勝手に追加されたりする。
急に Migration に書いてない userId カラムが増えてて「は?」ってなった。

Setting synchronize makes sure your entities will be synced with the database, every time you run the application.

命名規則が Laravel の Migration と違う

Laravel の Migration からインスピレーションを受けた的なことがライブラリの説明に書いてあった気がしまして、
勝手に命名規則は同じものを思ってたらそうじゃなかった。
例えば Laravel はテーブル名は複数形、Entity(EloquentModel) は単数形だけど、TypeORM そうなってない。
TypeORM の命名規則は DefaultNamingStrategy にあって継承することでカスタマイズは可能とのこと。

参考1
参考2
参考3

isPrimary: true だけだと auto increment じゃないっぽい

Migration でサンプル通り id カラムに isPrimary: true を指定したら当然 auto increment になると思ってたけど、そうじゃなかった。
Field 'id' doesn't have a default value? って言われる。
なので、isGenerated: truegenerationStrategy: 'increment'を指定する。
参考

import {MigrationInterface, QueryRunner, Table, TableIndex, TableColumn, TableForeignKey } from "typeorm";

export class QuestionRefactoringTIMESTAMP implements MigrationInterface {

    async up(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.createTable(new Table({
            name: "question",
            columns: [
                {
                    name: "id",
                    type: "int",
                    isPrimary: true,
                    isGenerated: true,
                    generationStrategy: 'increment'
                },
                {
                    name: "name",
                    type: "varchar",
                }
            ]
        }), true)

    }

    async down(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.dropTable("question");
    }

}

docker-compose を使う場合、host 名はDBコンテナ名

TypeORM に直接関係ないけど。
https://qiita.com/kajirikajiri/items/c2b557d884689a2b851e

nullable カラムの Entity プロパティ指定方法

Entity から Migration を作るコマンドがおそらくある

たぶんある。先にマイグレーションを書いたのでちゃんと確認してない。

Cannot use import statement outside a module

EntityMetadataNotFound: No metadata for “Hoge” was found

テーブル名とEntity名があってない(命名規則に則ってない)とか、コンパイル後のファイルが指定箇所以外に残ってるとかでこのエラーは解消できた気がする。

connection が生成できない

default がなんちゃらで connenction が生成できなかった。
keepConnectionAlive オプションを true 指定すれば良いとか見たけど。うまく行かなかった。
以下の実装で回避できた。

const connection = async (): Promise<Connection> => {
    try {
        return await getConnection();
    } catch (error) {
        return await createConnection();
    }
};

const connection = await connection();