nest.js + prisma + fastify + postgresqlでREST APIのCRUDを作成する

# 概要

node.jsのバックエンドフレームワークnest.jsを使ってREST APIのCRUDを作成します！
ORMにはprisma、DBはpostgresqlを使って作成します
この記事は[nest.js 公式](https://docs.nestjs.com/)を参考に作成しているので、
詳しく調べたい場合は公式ページの方をみて頂けると良いと思います！

# nest.jsのセットアップ

nest.jsのインストールにはnest.js用のCLIがあるのでそれを使用しましょう
npm、yarnのどちらかで使用できます

```bash
$ yarn global add @nestjs/cli
```

```bash
# プロジェクトを作る
$ nest new nestjs-prisma-crud

# カレントディレクトリに作る場合
$ nest new .
```

yarn を選択してインストール

```bash
? Which package manager would you ❤️  to use? yarn
```

とりあえずサーバー立ててみる

```bash
$ yarn start:dev
```

http://localhost:3000/ にアクセスして「Hello World!」って出てればセットアップ完了です！

# ディレクトリ説明

作成されたプロジェクトを見てみましょう！
nest.jsではプロジェクト作成時に`hello world`を返すapiサンプルが用意されています！

仕組みをざっくり説明すると
controllerでリクエストを受け取り、serviceで用意したデータ(`hello world`)を返します、controllerとserviceの紐付けはmoduleで行っているといった感じ

```
nestjs-prisma-crud
├── src
│   ├── app.controller.spec.ts
│   ├── app.controller.ts
│   ├── app.module.ts
│   ├── app.service.ts
│   └── main.ts
├── test
│   ├── app.e2e-spec.ts
│   └── jest-e2e.json
...省略
```

| ファイル名             | 説明                    |
| ---------------------- | ----------------------- |
| app.controller.spec.ts | テストファイル          |
| app.controller.ts      | APIのリクエスト受け取り |
| app.module.ts          | 依存関係を定義          |
| app.service.ts         | 処理メソッド            |
| main.ts                | プロジェクト設定        |
| test                   | E2Eテストコード         |

# 必要なライブラリインストール

別途でインストールが必要なライブラリをインストールしましょう
prismaとfastifyのライブラリを入れます
prismaを使う場合`pg`（postgresql）はインストールする必要がないみたい（typeormでは必要だったので）
`@nestjs/mapped-types`はこの先に出てくるdtoというファイルにデフォルトで使われる為入れてます！最終的には不要になります
`@nestjs/config`は.envを使うためのライブラリです、今回は使わないので入れなくても良いです！

```bash
$ yarn add prisma --dev
```

```bash
$ yarn add @prisma/client @nestjs/platform-fastify @nestjs/config @nestjs/mapped-types
```

# ライブラリの設定を行う

## prismaとDBのセットアップ

先ほど導入したprismaをセットアップしていきます
`init`をするとルートディレクトリに`prisma`と`.env`が作成されます
今回はlocaldbのpostgresqlを使って設定を行っていきます

```bash
$ yarn prisma init
```

```nestjs-prisma-crud/prisma/schema.prisma
datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

generator client {
  provider = "prisma-client-js"
}
```

USER、PASSWORD、HOST、PORT、DATABASEを自分の設定に書き換えてください

```nestjs-prisma-crud/.env
DATABASE_URL="postgresql://USER:PASSWORD@HOST:PORT/DATABASE"
```

## fastifyのセットアップ

fastifyを使用する為にmain.tsを修正します

```nestjs-prisma-crud/src/main.ts
import { NestFactory } from '@nestjs/core';
import {
  FastifyAdapter,
  NestFastifyApplication,
} from '@nestjs/platform-fastify';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create<NestFastifyApplication>(
    AppModule,
    new FastifyAdapter(),
  );
  await app.listen(3000);
}
bootstrap();
```

## 環境変数（env）のセットアップ

envの使用準備はConfigModuleを以下のように定義すれば
.envファイルを使用する事ができるようになります

```ts
import { ConfigModule } from '@nestjs/config';

...

process.env.HOGE
```

# prisma migrateでDBテーブルを作成

apiを作成する前にprismaのマイグレート機能を使ってDBテーブルを作成します
先ほど作成したschema.prismaにモデル定義を行います
schema.prismを使ってマイグレーションも行えるので、
今回は`task`というテーブルを作ってみます

```nestjs-prisma-crud/prisma/schema.prisma
model task {
  id      Int     @default(autoincrement()) @id
  title   String
  content String?
}
```

マイグレーションは以下のコマンドで実行することが可能です
マイグレーションの名前を聞かれるので`init`とでもつけておきましょう（初回なので）

```bash
$ yarn prisma migrate dev --preview-feature
✔ Name of migration … init
The following migration(s) have been created and applied from new schema changes:

migrations/
  └─ 20210301135608_init/
    └─ migration.sql
```

prismaフォルダにDB作成に使われたsqlなどが自動生成されます

```
nestjs-prisma-crud/prisma
├── migrations
│   ├── 20210301135608_init
│   │   └── migration.sql
│   └── migration_lock.toml
└── schema.prisma
```

DBクライアントから確認するとtaskというテーブルが作成されている事が確認できました

![スクリーンショット 2021-02-28 21.30.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478226/ead09428-ed3b-34b9-9c5c-a1a5cfb04c5f.png)

最後に下記のコメントを実行することで、
@prisma/clientからtaskテーブルを取り扱うことができるようになります

```bash
$ yarn prisma generate
```

# REST APIでCRUDを作成する

apiの作成準備が整ったのでCRUD APIを作ってみましょう

## nest.jsのCRUDジェネレーターを使ってみる

nest.jsには「CRUDジェネレーター」なるものがあるのでこれでAPIの雛形を作っていきます！
`nest g resource`で実行し質問形式で雛形が作れるので、
先ほど作成したテーブルに合わせて`task`というモデルでREST APIで作成してみます！

```bash
$ nest g resource
? What name would you like to use for this resource (plural, e.g., "users")? task
? What transport layer do you use? REST API
? Would you like to generate CRUD entry points? Yes
```

## 作成されるファイル

以下のようなファイルが生成されます
`controller`、`module`、`service`の他にもいくつかファイルができてますね
簡単に説明すると
dtoはパラメータの型定義やバリデーションに使用できます
ちなみにdtoは「Data Transfer Object」の略らしい
entitiesはオブジェクト定義に使用、TypeORMの場合にマイグレーションにも使えます

```
nestjs-prisma-crud/src/tasks
├── dto
│   ├── create-task.dto.ts
│   └── update-task.dto.ts
├── entities
│   └── task.entity.ts
├── task.controller.spec.ts
├── task.controller.ts
├── task.module.ts
├── task.service.spec.ts
└── task.service.ts
```

| ファイル名           | 説明                               |
| -------------------- | ---------------------------------- |
| create-task.dto.ts   | postで使用されるパラメータの型定義 |
| update-task.dto.ts   | putで使われるパラメータの型定義    |
| task.entity.ts       | オブジェクト定義                   |
| task.controller.ts   | APIのリクエスト受け取り            |
| task.module.ts       | 依存関係を定義                     |
| task.service.spec.ts | テストファイル                     |
| task.service.ts      | 処理メソッド                       |

## controllerでリクエスト受け取れるかチェック

controllerをみるとわかるように以下のようなルーティングができているので、
サーバー立ててチェックしてみます

- /task, POST
- /task, GET
- /task/:id, GET
- /task/:id, PUT
- /task/:id, DELETE

```bash
$ yarn start:dev

# {/task, GET}にリクエスト
$ curl localhost:3000/task
>> This action returns all
```

## CRUDを作る

リクエストが受け取れるのを確認できたので
APIでDB操作ができるようにしていきます！

まず初めにprisma/clientを使う準備をします
srcディレクトリ内に`prisma.service.ts`を作成して下記のコードを追加します
これだけで使用準備OK!

```nestjs-prisma-crud/src/prisma.service.ts
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService extends PrismaClient
  implements OnModuleInit, OnModuleDestroy {
  async onModuleInit() {
    await this.$connect();
  }

  async onModuleDestroy() {
    await this.$disconnect();
  }
}
```

次にに不要なファイルを消しちゃいましょう
今回はprismaが用意してくれるパラメータの型定義を使用するので、
dto、entitiesは使わないのでファイルごと消しちゃいました！

moduleに依存関係を定義します
providersにPrismaServiceを追加するだけですね！

```nestjs-prisma-crud/src/task/task.module.ts
import { Module } from '@nestjs/common';
import { TaskService } from './task.service';
import { TaskController } from './task.controller';
import { PrismaService } from './../prisma.service';

@Module({
  controllers: [TaskController],
  providers: [TaskService, PrismaService],
})
export class TaskModule {}
```

controllerを修正します！
serviceからレスポンスを受け取れるように型をつけます
`@prisma/client`からtaskの型定義を使っていますが、
これは先ほどtaskモデルを`schema.prisma`登録して`yarn prisma generate`をしてから使えるようになっています
どんな事ができるかは[prisma公式](https://www.prisma.io/docs/)をみてもらえると！

```nestjs-prisma-crud/src/task/task.controller.ts
import {
  Controller,
  Get,
  Post,
  Body,
  Put,
  Param,
  Delete,
} from '@nestjs/common';
import { TaskService } from './task.service';
import { task, Prisma } from '@prisma/client';

@Controller('task')
export class TaskController {
  constructor(private readonly taskService: TaskService) {}

  @Post()
  async create(@Body() data: Prisma.taskCreateInput): Promise<task> {
    return this.taskService.create(data);
  }

  @Get()
  async findAll(): Promise<task[]> {
    return this.taskService.findAll();
  }

  @Get(':id')
  async findOne(@Param('id') id: string): Promise<task> {
    return this.taskService.findOne(+id);
  }

  @Put(':id')
  async update(
    @Param('id') id: string,
    @Body() data: Prisma.taskUpdateInput,
  ): Promise<task> {
    return this.taskService.update(+id, data);
  }

  @Delete(':id')
  async remove(@Param('id') id: string): Promise<task> {
    return this.taskService.remove(+id);
  }
}
```

最後に実際に処理を行うserviceの方を実装していきます！
実装には[こちらのリポジトリ](https://github.com/prisma/prisma-examples)が参考になりました！

```nestjs-prisma-crud/src/task/task.service.ts
import { Injectable } from '@nestjs/common';
import { PrismaService } from './../prisma.service';
import { task, Prisma } from '@prisma/client';

@Injectable()
export class TaskService {
  constructor(private prisma: PrismaService) {}

  async create(data: Prisma.taskCreateInput): Promise<task> {
    return this.prisma.task.create({ data });
  }

  async findAll(): Promise<task[]> {
    return this.prisma.task.findMany();
  }

  async findOne(id: number) {
    return this.prisma.task.findUnique({
      where: { id: id },
    });
  }

  async update(id: number, data: Prisma.taskUpdateInput): Promise<task> {
    return this.prisma.task.update({
      where: { id: id },
      data,
    });
  }

  async remove(id: number): Promise<task> {
    return this.prisma.task.delete({
      where: { id: id },
    });
  }
}
```

これで実装は以上です！
次は早速作ったAPIを動かしてみます！

# APIを動かしてみる

curlでAPIにリクエストを投げてレスポンスを確認してみます！

```bash
$ yarn start:dev
```

- task Post()

```bash
$ curl -X POST localhost:3000/task -H 'Content-Type:application/json' -d "{\"title\":\"あいうえお\", \"content\": \"アイウエオ\"}"
```

![スクリーンショット 2021-03-04 21.51.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478226/c3dc89cb-4143-eadd-cbfe-219792eb90f0.png)

- task Get()

```bash
$ curl -X GET localhost:3000/task
```

```json
[
  {"id":1,"title":"あいうえお","content":"アイウエオ"},
  {"id":2,"title":"かきくけこ","content":"カキクケコ"},
  {"id":3,"title":"さしすせそ","content":"サシスセソ"}
]
```

- task Get(':id')

```bash
$ curl -X GET localhost:3000/task/1
```

```json
{"id":1,"title":"あいうえお","content":"アイウエオ"}
```

- task Put(':id')

```bash
$ curl -X PUT localhost:3000/task/1 -H 'Content-Type:application/json' -d "{\"title\":\"あああああ\", \"content\": \"いいいいい\"}"
```

```json
{"id":1,"title":"あああああ","content":"いいいいい"}
```

- task Delete(':id')

```bash
$ curl -X DELETE localhost:3000/task/1
```

```json
{"id":1,"title":"あああああ","content":"いいいいい"}
```

# gitリポジトリ

今回はnest.jsの一部しか使っていませんが、
割と簡単にAPIを作る事ができました！
prismaも導入が楽で、使用方法もシンプルでよかったです！
prismaはgraphQLとの相性も良いらしく、nest.jsでgraphQLの雛形も作れるので、
graphQLで作ってみても良いですね！

今回使ったリポジトリはこちらになります！
https://github.com/git-gen/nestjs-prisma-crud