19
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

nestjsとTypeORMに入門した

Last updated at Posted at 2020-03-20

はじめに

node.jsのフレームワークで最近勢いのあるnestjsと、これまた人気が出てるORMのTypeORMを触ってみました。
node.jsのフレームワークは薄いものが多いですが、それらとは違って結構多機能で便利に感じたので、備忘録がてら記事にします。

typescript、 eslint、 prettier、 jest、 ホットリロードなどが初めから設定された状態で開発できる

nest new <project-name> とするだけで初めからいろんなツールチェインが入った状態で開発ができます。
あれを入れて、これを入れて、、とやる手間が省けて楽でした。

class-validatorを使ってリクエストボディのバリデーションが書ける

nestjsではリクエストで飛んでくるボディやクエリストリングなどのことをDTOと読んでます。
このDTOはクラスで定義するのですが、そのクラスのプロパティにclass-validatorのデコレータをペタペタ追加していくことで、柔軟なバリデーションをかけることができます。

class CreateParams {
  @IsNotEmpty()
  hoge: string;
}

@Controller('sample')
export class SampleController {
  constructor(private sampleService: SampleService) {}

  // これで、「POST /sample」ではbody内にhoge情報が必須になる
  @Post()
  create(@Body() params: CreateParams) {
    return this.sampleService.create(params);
  }
}

またこのバリデーションを有効にするためには以下の記述が必要でした。
参考: https://docs.nestjs.com/pipes

main.ts
  app.useGlobalPipes(new ValidationPipe());

swaggerを書くのが簡単

swaggerの定義をymlで書くのって割とめんどくさいと思っちゃうんですが、これもデコレーターを使って簡単に書けます。

こんな感じの定義をしておけば、


export class FindByIdParams {
  @ApiProperty({
    description: 'sample_idでの検索',
    type: Number,
  })
  @IsNumberString()
  sample_id: number;
}

こんな感じで表示されます。
スクリーンショット 2020-03-20 14.16.04.png

他の書き方とかの詳細はこちら: https://docs.nestjs.com/recipes/swagger#openapi-swagger

トランザクションを貼りたい

nestjsとTypeORMのカスタムリポジトリを使っている状態でトランザクションを貼るにはどうやるのがいいのか探していたらこの記事が見つかりました。

こんな感じでトランザクションを貼れました。

@Injectable()
export class SampleService {
  constructor(private sampleRepository: SampleRepository, private connection: Connection) {}

  create(params: CreateParams): Promise<Sample> {
    return this.connection.transaction(async (manager) => {
      const sampleRepository = manager.getCustomRepository(SampleRepository);

      return sampleRepository.save({
        ...params,
        created_at: new Date(),
        updated_at: new Date(),
      });
    });
  }
}

TypeORMはトランザクションを貼るにもいろんな方法を提供してくれてます。(3種類くらい?)
ただ、nestjsの方の記事で見つけたのですが、 @Transaction のデコレータを使ってtransactionを貼るのはおすすめしないみたいです。

スクリーンショット 2020-03-19 14.43.07.png

TypeORMのマイグレーションが便利

TypeORMはコードの状態とDBの状態を比較して、そのコードの状態にするためのマイグレーションファイルを自動で作ってくれます。

例えば、こういうsamplesのentityを定義するコードがあるとして、DB上にはsamplesテーブルがなかった場合、


import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity('samples')
export class Sample {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  hoge: string;

  @Column()
  created_at: Date;

  @Column()
  updated_at: Date;
}

samplesをCREATEするマイグレーションファイルを自動で作ってくれます。


import {MigrationInterface, QueryRunner} from "typeorm";

export class CreateSamples1584670197350 implements MigrationInterface {
    name = 'CreateSamples1584670197350'

    public async up(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`CREATE TABLE "samples" ("id" SERIAL NOT NULL, "hoge" character varying NOT NULL, "created_at" TIMESTAMP NOT NULL, "updated_at" TIMESTAMP NOT NULL, CONSTRAINT "PK_d68b5b3bd25a6851b033fb63444" PRIMARY KEY ("id"))`, undefined);
    }

    public async down(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`DROP TABLE "samples"`, undefined);
    }

}

このCREATE TABLEの実行後に、entityにカラム追加をした場合は、


import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity('samples')
export class Sample {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  hoge: string;

  @Column()
  huga: string; // 追加

  @Column()
  created_at: Date;

  @Column()
  updated_at: Date;
}

差分となるALTER TABLEのマイグレーションファイルを自動で作ってくれます。

import {MigrationInterface, QueryRunner} from "typeorm";

export class AddHugaColumnToSamples1584682435530 implements MigrationInterface {
    name = 'AddHugaColumnToSamples1584682435530'

    public async up(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`ALTER TABLE "samples" ADD "huga" character varying NOT NULL`, undefined);
    }

    public async down(queryRunner: QueryRunner): Promise<void> {
        await queryRunner.query(`ALTER TABLE "samples" DROP COLUMN "huga"`, undefined);
    }

}

また、TypeORMの設定項目synchronize を有効にしておくと、コードのentityに何か変更があった場合、マイグレーションを自動実行してくれるようになるので、開発中にマイグレーション周りの作業をほとんどしなくてよくなります。

最後に

他にもnestjsは色々機能があるので、いいと思ったのがあれば加筆していきます。

今回試してみたコードはこちらにあげてます。
https://github.com/pokotyan/nestjs-sample

19
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
19
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?