NestJS + TypeORM で MariaDB に接続するまで

ライブラリのインストール

shell
npm install --save @nestjs/typeorm typeorm mysql2

環境変数を読み込む準備

以下の記事を参考にして、 .env からデータベースのパラメータを設定する準備をしてください。
本番環境と開発環境ではパラメータが異なるはずなので、環境変数で出し分けが必要なためです。

.env ファイルを作成

.env にデータベースの設定情報を記載します。

.env
# App Configration
APP_PORT=3000

# Database ORM configuration
TYPEORM_CONNECTION=mysql
TYPEORM_HOST=localhost
TYPEORM_PORT=3306
TYPEORM_USERNAME=nestjs
TYPEORM_PASSWORD=password
TYPEORM_DATABASE=nestjs
TYPEORM_SYNCHRONIZE=false

app.module.ts の設定

app.module.ts を以下のように編集し、 TypeORM の依存関係を定義します。

app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ConfigModule, ConfigService } from '@nestjs/config';
import configration from './config/configration';
import { TypeOrmModule } from '@nestjs/typeorm';
import { NotesModule } from './notes/notes.module';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      load: [configration],
    }),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: (configService: ConfigService) => ({
        type: 'mariadb',
        host: configService.get<string>('database.host'),
        port: configService.get<number>('database.port'),
        username: configService.get<string>('database.username'),
        password: configService.get<string>('database.password'),
        database: configService.get<string>('database.database'),
        synchronize: configService.get<boolean>('database.synchronize'),
        autoLoadEntities: true,
        charset: 'utf8mb4',
      }),
      inject: [ConfigService],
    }),
    NotesModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Entity を作成する

新しい Entity を作成してみます。

notes/entities/note.entity.ts
import { Column, Entity } from 'typeorm';

@Entity('notes')
export class Note {
  @Column('uuid', { primary: true, generated: 'uuid' })
  id: string;

  @Column('varchar', { length: 255 })
  title: string;
}

サブモジュールに Entityを登録する

notes/notes.module.ts に作成した Entity を登録します。

notes/notes.module.ts
import { TypeOrmModule } from '@nestjs/typeorm';
import { Note } from './entities/note.entity';
import { Module } from '@nestjs/common';

@Module({
  imports: [TypeOrmModule.forFeature([Note])],
})
export class NotesModule {}

このサブモジュールは app.module.ts で読み込みます。 imports の中の NotesModule です。

app.module.ts
imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      load: [configration],
    }),
    TypeOrmModule.forRootAsync({
      imports: [ConfigModule],
      useFactory: (configService: ConfigService) => ({
        type: 'mariadb',
        host: configService.get<string>('database.host'),
        port: configService.get<number>('database.port'),
        username: configService.get<string>('database.username'),
        password: configService.get<string>('database.password'),
        database: configService.get<string>('database.database'),
        synchronize: configService.get<boolean>('database.synchronize'),
        autoLoadEntities: true,
        charset: 'utf8mb4',
      }),
      inject: [ConfigService],
    }),
    NotesModule,
  ],

マイグレーションを実行する

ライブラリのインストール

shell
npm i -g typeorm
npm install ts-node --save-dev

package.json の追記

package.jsonscripts に以下を追加します。

package.json
    "migration:gen": "ts-node -r dotenv/config -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:generate -d ormconfig.ts",
    "migration:run": "ts-node -r dotenv/config -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:run -d ormconfig.ts",
    "migration:revert": "ts-node -r dotenv/config -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:revert -d ormconfig.ts",
    "migration:show": "ts-node -r dotenv/config -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:show -d ormconfig.ts",

scripts 全体は以下のようになっています。

package.json
  "scripts": {
    "build": "nest build",
    "format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
    "start": "nest start -b swc",
    "start:dev": "nest start --watch -b swc",
    "start:debug": "nest start --debug --watch",
    "start:prod": "node dist/main",
    "migration:gen": "ts-node -r dotenv/config -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:generate -d ormconfig.ts",
    "migration:run": "ts-node -r dotenv/config -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:run -d ormconfig.ts",
    "migration:revert": "ts-node -r dotenv/config -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:revert -d ormconfig.ts",
    "migration:show": "ts-node -r dotenv/config -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:show -d ormconfig.ts",
    "lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
    "test": "jest",
    "test:watch": "jest --watch",
    "test:cov": "jest --coverage",
    "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
    "test:e2e": "jest --config ./test/jest-e2e.json"
  },

ormconfig.ts を作成

マイグレーション実行時に読み込む設定ファイルを用意します。

ormconfig.ts
import { DataSource } from 'typeorm';

export const dataSource: DataSource = new DataSource({
  type: 'mysql',
  host: process.env.TYPEORM_HOST,
  port: process.env.TYPEORM_PORT
    ? parseInt(process.env.TYPEORM_PORT, 10)
    : 3306,
  username: process.env.TYPEORM_USERNAME,
  password: process.env.TYPEORM_PASSWORD,
  database: process.env.TYPEORM_DATABASE,
  synchronize: process.env.TYPEORM_SYNCHRONIZE === 'true',
  entities: [__dirname + '/**/*.entity{.ts,.js}'],
  migrations: [__dirname + '/migrations/**/*{.ts,.js}'],
  timezone: 'Z',
  charset: 'utf8mb4',
});

マイグレーションのコマンド

  • マイグレーションファイルの生成
  • マイグレーションのステータス確認
  • マイグレーションの実行
shell
npm run migration:gen -- ./migrations/CreateNoteTable
npm run migration:show
npm run migration:run

npm run migration:gen -- ./migrations/CreateNoteTable では、生成されるマイグレーションファイルの名前を指定しています。

参考までに、コマンドの実行ログを載せます。

shell
$ npm run migration:show

> tejun-note-backend@0.0.1 migration:show
> ts-node -r dotenv/config -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:show -d ormconfig.ts

[ ] CreateNoteTable1691912295169
fj@fjshnoMacBook-Pro: ~/workspace/dev/tejun-note-backend (main *%=)
$ npm run migration:run

> tejun-note-backend@0.0.1 migration:run
> ts-node -r dotenv/config -r tsconfig-paths/register ./node_modules/typeorm/cli.js migration:run -d ormconfig.ts

query: SELECT VERSION() AS `version`
query: SELECT * FROM `INFORMATION_SCHEMA`.`COLUMNS` WHERE `TABLE_SCHEMA` = 'nestjs' AND `TABLE_NAME` = 'migrations'
query: SELECT * FROM `nestjs`.`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 `notes` (`id` varchar(36) NOT NULL, `title` varchar(255) NOT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB
query: INSERT INTO `nestjs`.`migrations`(`timestamp`, `name`) VALUES (?, ?) -- PARAMETERS: [1691912295169,"CreateNoteTable1691912295169"]
Migration CreateNoteTable1691912295169 has been  executed successfully.
query: COMMIT

コマンドを実行すると、データベースにテーブルが作成できます。

参考

データベースを立ち上げるために使った docker-compose.yml

docker-compose.yml
version: '3'

services:
  mariadb:
    image: 'bitnami/mariadb:10.6'
    ports:
      - '3306:3306'
    volumes:
      - 'mariadb_data:/bitnami/mariadb'
    environment:
      - ALLOW_EMPTY_PASSWORD=yes
      - MARIADB_DATABASE=nestjs
      - MARIADB_USER=nestjs
      - MARIADB_PASSWORD=password
      - TZ="Asia/Tokyo"

volumes:
  mariadb_data:
    driver: local