NestJS × TypeORM 0.3 でCLIからmigrationする
前提
下記記事の構成で環境構築した前提で進める。
NestJS(Fastify) × TypeORM × PostgreSQL × Dockerで環境構築
手順
1. Userエンティティの実装
nest g res users --no-spec
? What transport layer do you use? REST API? Would you like to generate CRUD entry points? Yes
まずCRUD generatorでusers
リソースを追加する。
(※本記事で使用するのはuser.entity.ts
のみなので、その他に作成されたuser.controller.ts
やuser.service.ts
、DTOなどは、クラスの中身を空にするなり削除するなりして問題ない)
import { Entity, PrimaryGeneratedColumn, CreateDateColumn, UpdateDateColumn, Timestamp, Column,} from 'typeorm';
@Entity('users')export class User { @PrimaryGeneratedColumn() readonly id?: number;
@CreateDateColumn({ name: 'create_at' }) readonly createdAt?: Timestamp;
@UpdateDateColumn({ name: 'updated_at' }) readonly updatedAt?: Timestamp;
@Column({ unique: true }) email: string;
@Column({ name: 'hashed_password' }) hashedPassword: string;}
user.entity.ts
にて、上記のようにカラムを設定する。
2. データソースの設定
import { Module } from '@nestjs/common';import { TypeOrmModule } from '@nestjs/typeorm';import { ConfigService } from '@nestjs/config';
export const ENTITIES_DIR = 'dist/**/*.entity.js';export const MIGRATION_FILES_DIR = 'dist/database/migrations/*.js';
@Module({ imports: [ TypeOrmModule.forRootAsync({ inject: [ConfigService], useFactory: (configService: ConfigService) => ({ type: 'postgres', host: configService.get<string>('DATABASE_HOST'), database: configService.get<string>('DATABASE_NAME'), username: configService.get<string>('DATABASE_USER'), password: configService.get<string>('DATABASE_PASSWORD'), port: Number(configService.get<string>('DATABASE_PORT')), entities: [], entities: [ENTITIES_DIR], synchronize: false, migrations: [MIGRATION_FILES_DIR], }), }), ],})export class DatabaseModule {}
今回作成していくデータソースファイルだが環境構築で作成したsrc/database/database.module.ts
と重複する設定があるため、上記のように修正する。
- 先ほど定義した
User
Entityを指定しつつ、変数化してexportする migrations
にマイグレーションファイルのパスを指定
import { DataSource } from 'typeorm';import { ENTITIES_DIR, MIGRATION_FILES_DIR } from './database.module';import 'dotenv/config';
export const AppDataSource = new DataSource({ type: 'postgres', host: process.env.DATABASE_HOST, database: process.env.DATABASE_NAME, username: process.env.DATABASE_USER, password: process.env.DATABASE_PASSWORD, port: Number(process.env.DATABASE_PORT), entities: [ENTITIES_DIR], synchronize: false, migrations: [MIGRATION_FILES_DIR],});
データソースファイルであるsrc/database/database-source.ts
を作成し、先ほどexportしたものをこちらでも指定する。
3. マイグレーションファイルの作成
docker exec -it app sh
mkdir -p src/database/migrations
コンテナ内に入り、専用のディレクトリを作成。
npx typeorm-ts-node-commonjs migration:generate -d src/database/database-source.ts --pretty src/database/migrations/CreateUser
上記コマンドを叩く。(--pretty
を付与し、作成されるマイグレーションファイル内のSQL文を複数行にしている)
import { MigrationInterface, QueryRunner } from "typeorm";
export class CreateUser1695999831504 implements MigrationInterface { name = 'CreateUser1695999831504'
public async up(queryRunner: QueryRunner): Promise<void> { await queryRunner.query(` CREATE TABLE "user" ( "id" SERIAL NOT NULL, "create_a" TIMESTAMP NOT NULL DEFAULT now(), "updated_at" TIMESTAMP NOT NULL DEFAULT now(), "email" character varying NOT NULL, "hashed_password" character varying NOT NULL, CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id") ) `); }
public async down(queryRunner: QueryRunner): Promise<void> { await queryRunner.query(` DROP TABLE "user" `); }
}
src/database/migrations/xxxxxxxxxxxxx-CreateUser.ts
が作成され、今回追加したUser
のテーブルをCREATEするSQLが出力されていることが確認できる。
4. マイグレーションの実行
npx typeorm-ts-node-commonjs migration:run -d src/database/database-source.ts
上記コマンドを叩く。
query: SELECT * FROM current_schema()query: SELECT version();query: SELECT * FROM "information_schema"."tables" WHERE "table_schema" = 'public' AND "table_name" = 'migrations'query: CREATE TABLE "migrations" ("id" SERIAL NOT NULL, "timestamp" bigint NOT NULL, "name" character varying NOT NULL, CONSTRAINT "PK_8c82d7f526340ab734260ea46be" PRIMARY KEY ("id"))query: SELECT * FROM "migrations" "migrations" ORDER BY "id" DESC0 migrations are already loaded in the database.1 migrations were found in the source code.1 migrations are new migrations must be executed.query: START TRANSACTIONquery: CREATE TABLE "user" ( "id" SERIAL NOT NULL, "create_a" TIMESTAMP NOT NULL DEFAULT now(), "updated_at" TIMESTAMP NOT NULL DEFAULT now(), "email" character varying NOT NULL, "hashed_password" character varying NOT NULL, CONSTRAINT "PK_cace4a159ff9f2512dd42373760" PRIMARY KEY ("id") )
query: INSERT INTO "migrations"("timestamp", "name") VALUES ($1, $2) -- PARAMETERS: [1695999831504,"CreateUser1695999831504"]Migration CreateUser1695999831504 has been executed successfully.query: COMMIT
先ほど作成したマイグレーションファイルに沿ってクエリが実行された。
pgAdminを見てみると、users
テーブルが作成されカラムも定義されていることが確認できる。
参考