Closed6

NestJS Prisma training

waddy_uwaddy_u

Getting start

https://docs.nestjs.com/recipes/prisma#prisma

コード書いて起動しようとしたらエラー

[Nest] 22573  - 11/13/2021, 11:53:53 PM     LOG [NestFactory] Starting Nest application...
[Nest] 22573  - 11/13/2021, 11:53:53 PM   ERROR [ExceptionHandler] @prisma/client did not initialize yet. Please run "prisma generate" and try to import it again.
In case this error is unexpected for you, please report it in https://github.com/prisma/prisma/issues

prisma generate してもだめ。

PrismaClient の型が違いました

prisma.service.ts
import { INestApplication, Injectable, OnModuleInit } from '@nestjs/common';
- import { PrismaClient } from '@prisma/client/scripts/default-index';
+ import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  /**
   * https://docs.nestjs.com/recipes/prisma#prisma
   * onModuleInitはオプションです。
   * 省略した場合、Prismaはデータベースへの最初の呼び出しで遅延接続します。
   * Prismaには接続を破棄する独自のシャットダウンフックがあるので、
   * onModuleDestroyは使いません。
   */
  async onModuleInit(): Promise<any> {
    await this.$connect();
  }

  async enableShutDownHooks(app: INestApplication) {
    this.$on('beforeExit', async () => {
      await app.close();
    });
  }
}

Intellij の型補完を信じすぎた。

リクエストを送る

はい

はい。大丈夫そう。

https://github.com/cm-wada-yusuke/gql-nest-prisma-training/tree/main/hello-prisma

waddy_uwaddy_u

最初、こうする

既存のコードをコピペしてまったく新しいデータベースで試したい場合。まずはコードをコピーする。

インストール

yarn install

データベースを立ち上げる

docker compose up --remove-orphans

マイグレーションを適用する

コピーしたからなどの理由で migrations/ が存在する場合は削除すること。

yarn prisma migrate dev --name init

DBスキーマから型ファイルを生成

 yarn prisma generate
waddy_uwaddy_u

モデル(ドメイン?)ごとに切ってみる

わからないところはとりあえずファイルをわけるようにした。

https://github.com/cm-wada-yusuke/gql-nest-prisma-training/tree/main/using-domain-object/src/models/posts

src/models/
└── posts
    ├── dto
    │   └── find-post.dto.ts
    ├── entities
    │   └── post.entity.ts
    ├── post.controller.spec.ts
    ├── post.controller.ts
    ├── post.module.ts
    ├── post.service.ts
    ├── repositories
    │   └── post.repository.ts
    └── serializers
        └── published-post.serializer.ts
waddy_uwaddy_u

DTO でバリデーション

/post/:id のエンドポイントに対してバリデーションしてみる。

https://docs.nestjs.com/techniques/validation

ここをみながら。

yarn add class-validator class-transformer

とりあえずオプションはなしにして、Pipeを追加する。

amin.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { PrismaService } from './prisma.service';
import { ValidationPipe } from '@nestjs/common';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

+  // バリデーションPipeを追加
+  app.useGlobalPipes(new ValidationPipe());

  // Primsaがアプリケーションのシャットダウン前に `process.exit()` に反応してシャットダウンされてしまう仕様なので
  // アプリケーションのシャットダウンフックに反応するよう設定を追加する
  const prismaService: PrismaService = app.get(PrismaService);
  await prismaService.enableShutDownHooks(app);

  await app.listen(3001);
}

bootstrap();

DTO にバリデーションを追加。あれ、この @IsNumberString ってのはどこにあるんだ。

https://github.com/typestack/class-validator#validation-decorators

こっちっぽい。今回は数値に変換したいので @IsNumberString にする。

おお〜ちゃんとバリデーションされてるぅ〜。

エラーメッセージをカスタマイズ

したくなるよね。

find-post.dto.ts
import { IsNumberString } from 'class-validator';

export class FindPostDto {
  @IsNumberString(
    {},
    {
      message: 'IDは数値です',
    },
  )
  id: string;
}

これでいいらしい。

よきよき。

waddy_uwaddy_u

Service のパラメータは何(どのレイヤ)にするのがよいか?

DTOがいいと思う。Entity レイヤにもうひとつ更新や作成用のドメインオブジェクトを用意するのはさすがに大変。

  • コントローラーはリクエストをDTOに変換する(自動でtransformできるならそれで)
  • コントローラーはサービスを呼び出す
  • サービスはDTOを受け取り、エンティティを返すか、外界への副作用を実行するためのリポジトリまたは他のサービスを呼び出す
  • コントローラーはエンティティを受け取り、シリアライズして返す
このスクラップは2021/12/01にクローズされました