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" DESC 0 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 TRANSACTION 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") ) query: INSERT INTO "migrations"("timestamp", "name") VALUES ($1, $2) -- PARAMETERS: [1695999831504,"CreateUser1695999831504"] Migration CreateUser1695999831504 has been executed successfully. query: COMMIT
先ほど作成したマイグレーションファイルに沿ってクエリが実行された。
pgAdminを見てみると、users
テーブルが作成されカラムも定義されていることが確認できる。