高瀬博道の技術ブログ

高瀬博道の技術ブログです。

NestJS でデータベース操作できるようにする - 6

前回

takasehiromichiex.com

TypeORMのマイグレーションについて

モデルが変更される場合、それをデータベースに同期する必要があります。

synchronize: trueというオプションをルートモジュールで設定していましたが、これはEntityに基づいてテーブルを自動生成してくれるオプションです。

そのため、本番環境ではこのオプションはfalseに設定しておくべきです。

Sequelize統合

TypeORMを使用する代わりに、Sequelize ORMを使用することもできます。

以下をインストールします。

$ npm install --save @nestjs/sequelize sequelize sequelize-typescript mysql2
$ npm install --save-dev @types/sequelize

ルートモジュールに組み込みます。

src/app.module.ts
import { MiddlewareConsumer, Module, NestModule } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";
import { CatsController } from "./cats/cats.controller";
import { CatsModule } from "./cats/cats.module";
import { logger } from "./middleware/logger/logger.middleware";
import { UserModule } from "./user/user.module";
import { SequelizeModule } from "@nestjs/sequelize";

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: "mysql",
      host: "localhost",
      port: 3306,
      username: "root",
      password: "root",
      database: "test",
      synchronize: true,
      autoLoadEntities: true,
    }),
    SequelizeModule.forRoot({
      dialect: "mysql",
      host: "localhost",
      port: 3306,
      username: "root",
      password: "root",
      database: "test",
      models: [],
    }),
    CatsModule,
    UserModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(logger).forRoutes(CatsController);
  }
}

SequelizeModule.forRootを追加しました。

サービスにDIしてみます。

src/user/user.service.ts
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Sequelize } from "sequelize";
import { DataSource, DeleteResult, Repository } from "typeorm";
import { CreateUserDto } from "./dto/create-user.dto";
import { UpdateUserDto } from "./dto/update-user.dto";
import { User } from "./entities/user.entity";

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(User) private userRepository: Repository<User>,
    private dataSource: DataSource,
    private sequelize: Sequelize
  ) {}

Sequelize モデル

SequelizeはActive Recordパターンで実装します。

このパターンでは、モデルクラスを直接使用してデータベースを操作します。

モデルを作成します。

src/user/user.model.ts
import { Column, PrimaryKey, Table, Model } from "sequelize-typescript";

@Table
export class User extends Model {
  @PrimaryKey
  id: number;
  @Column
  firstName: string;
  @Column
  lastName: string;
  @Column({ defaultValue: true })
  isActive: boolean;
}

ルートモジュールに作成したモデルを追加します。

src/app.module.ts
import { MiddlewareConsumer, Module, NestModule } from "@nestjs/common";
import { TypeOrmModule } from "@nestjs/typeorm";
import { AppController } from "./app.controller";
import { AppService } from "./app.service";
import { CatsController } from "./cats/cats.controller";
import { CatsModule } from "./cats/cats.module";
import { logger } from "./middleware/logger/logger.middleware";
import { UserModule } from "./user/user.module";
import { SequelizeModule } from "@nestjs/sequelize";
import { User } from "./user/user.model";

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: "mysql",
      host: "localhost",
      port: 3306,
      username: "root",
      password: "root",
      database: "test",
      synchronize: true,
      autoLoadEntities: true,
    }),
    SequelizeModule.forRoot({
      dialect: "mysql",
      host: "localhost",
      port: 3306,
      username: "root",
      password: "root",
      database: "test",
      models: [User],
    }),
    CatsModule,
    UserModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule implements NestModule {
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(logger).forRoutes(CatsController);
  }
}

その後、ユーザモジュールに組み込みます。

src/user/user.module.ts
import { Module } from "@nestjs/common";
import { UserService } from "./user.service";
import { UserController } from "./user.controller";
import { TypeOrmModule } from "@nestjs/typeorm";
import { UserSchema } from "./user.schema";
import { PhotoSchema } from "src/photo/photo.schema";
import { SequelizeModule } from "@nestjs/sequelize";
import { User } from "./user.model";

@Module({
  imports: [
    TypeOrmModule.forFeature([UserSchema, PhotoSchema]),
    SequelizeModule.forFeature([User]),
  ],
  controllers: [UserController],
  providers: [UserService],
})
export class UserModule {}

SequelizeModule.forFeature([User]),を追加しました。

これをサービスに適用できます。

src/user/user.service.ts
import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { DataSource, Repository } from "typeorm";
import { CreateUserDto } from "./dto/create-user.dto";
import { UpdateUserDto } from "./dto/update-user.dto";
import { User } from "./entities/user.entity";
import { User as UserModel } from "./user.model";

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(User) private userRepository: Repository<User>,
    private dataSource: DataSource,
    private userModel: typeof UserModel
  ) {}

  create(createUserDto: CreateUserDto) {
    return "This action adds a new user";
  }

  async createMany(users: User[]) {
    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();

    try {
      await queryRunner.manager.save(users[0]);
      await queryRunner.manager.save(users[1]);
      await queryRunner.commitTransaction();
    } catch (error) {
      await queryRunner.rollbackTransaction();
    } finally {
      await queryRunner.release();
    }
  }

  async findAll(): Promise<UserModel[]> {
    return this.userModel.findAll();
  }

  findOne(id: number): Promise<UserModel> {
    return this.userModel.findOne({
      where: {
        id,
      },
    });
  }

  update(id: number, updateUserDto: UpdateUserDto) {
    return `This action updates a #${id} user`;
  }

  async remove(id: number): Promise<void> {
    const user = await this.findOne(id);
    await user.destroy();
  }
}

全体的にuserModelに変更しました。

まとめ

ここまでで、

  • TypeORMのマイグレーションについて
  • Sequelize統合について
  • Sequelize モデルについて

を学びました。

データベース操作できるようになったので、これで終わろうと思います。

コード

今回のコードは、以下に格納しました。

github.com