はじめに
node.jsのフレームワークで最近勢いのあるnestjsと、これまた人気が出てるORMのTypeORMを触ってみました。
node.jsのフレームワークは薄いものが多いですが、それらとは違って結構多機能で便利に感じたので、備忘録がてら記事にします。
typescript,eslint,prettier,jest,ホットリロードなどが初めから設定された状態で開発できる
nest new <project-name>
とするだけで初めからいろんなツールチェインが入った状態で開発ができます。
あれを入れて、これを入れて、、とやる手間が省けて楽でした。
class-validatorを使ってリクエストボディのバリデーションが書ける
nestjsではリクエストで飛んでくるボディやクエリストリングなどのことをDTOと読んでます。
このDTOはクラスで定義するのですが、そのクラスのプロパティにclass-validatorのデコレータをペタペタ貼ってくことで、柔軟なバリデーションをかけることができます。
classCreateParams{@IsNotEmpty()hoge:string;}@Controller('sample')exportclassSampleController{constructor(privatesampleService:SampleService){}// これで、「POST /sample」ではbody内にhoge情報が必須になる@Post()create(@Body()params:CreateParams){returnthis.sampleService.create(params);}}
またこのバリデーションを有効にするためには以下の記述が必要でした。
参考: https://docs.nestjs.com/pipes
app.useGlobalPipes(newValidationPipe());
swaggerを書くのが簡単
swaggerの定義をymlで書くのって割とめんどくさいと思っちゃうんですが、これもデコレーターを使って簡単に書けます。
こんな感じの定義をしておけば、
exportclassFindByIdParams{@ApiProperty({description:'sample_idでの検索',type:Number,})@IsNumberString()sample_id:number;}
他の書き方とかの詳細はこちら: https://docs.nestjs.com/recipes/swagger#openapi-swagger
マイグレーションを貼りたい
nestjsとTypeORMのカスタムリポジトリを使っている状態でマイグレーションを貼るにはどうやるのがいいのか探していたらこの記事が見つかりました。
こんな感じでトランザクションを貼れました。
@Injectable()exportclassSampleService{constructor(privatesampleRepository:SampleRepository,privateconnection:Connection){}create(params:CreateParams):Promise<Sample>{returnthis.connection.transaction(async(manager)=>{constsampleRepository=manager.getCustomRepository(SampleRepository);returnsampleRepository.save({...params,created_at:newDate(),updated_at:newDate(),});});}}
TypeORMはトランザクションを貼るにもいろんな方法を提供してくれてます。(3種類くらい?)
ただ、nestjsの方の記事で見つけたのですが、@Transactionのデコレータを使ってtransactionを貼るのはおすすめしないみたいです。
TypeORMのマイグレーションが便利
TypeORMはコードの状態とDBの状態を比較して、そのコードの状態にするためのマイグレーションファイルを自動で作ってくれます。
例えば、こういうsamplesのentityを定義するコードがあるとして、DB上にはsamplesテーブルがなかった場合、
import{Entity,Column,PrimaryGeneratedColumn}from'typeorm';@Entity('samples')exportclassSample{@PrimaryGeneratedColumn()id:number;@Column()hoge:string;@Column()created_at:Date;@Column()updated_at:Date;}
samplesをCREATEするマイグレーションファイルを自動で作ってくれます。
import{MigrationInterface,QueryRunner}from"typeorm";exportclassCreateSamples1584670197350implementsMigrationInterface{name='CreateSamples1584670197350'publicasyncup(queryRunner:QueryRunner):Promise<void>{awaitqueryRunner.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);}publicasyncdown(queryRunner:QueryRunner):Promise<void>{awaitqueryRunner.query(`DROP TABLE "samples"`,undefined);}}
このsamplesのentityにカラム追加をした場合は、
import{Entity,Column,PrimaryGeneratedColumn}from'typeorm';@Entity('samples')exportclassSample{@PrimaryGeneratedColumn()id:number;@Column()hoge:string;@Column()huga:string;// 追加@Column()created_at:Date;@Column()updated_at:Date;}
ALTER TABLEのマイグレーションファイルを自動で作ってくれます。
import{MigrationInterface,QueryRunner}from"typeorm";exportclassAddHugaColumnToSamples1584682435530implementsMigrationInterface{name='AddHugaColumnToSamples1584682435530'publicasyncup(queryRunner:QueryRunner):Promise<void>{awaitqueryRunner.query(`ALTER TABLE "samples" ADD "huga" character varying NOT NULL`,undefined);}publicasyncdown(queryRunner:QueryRunner):Promise<void>{awaitqueryRunner.query(`ALTER TABLE "samples" DROP COLUMN "huga"`,undefined);}}
また、TypeORMの設定項目の synchronize
を有効にしておくと、コードのentityに何か変更があった場合、
- 差分のマイグレーションファイルを自動生成
- マイグレーションを自動実行
してくれるようになるので、開発中にマイグレーション周りの作業をほとんどしなくてよくなります。
最後に
他にもnestjsは色々機能があるので、いいと思ったのがあれば加筆していきます。
今回試してみたコードはこちらにあげてます。
https://github.com/pokotyan/nestjs-sample