LoginSignup
26
26

More than 3 years have passed since last update.

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

Last updated at Posted at 2021-03-02

概要

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

nest.jsのセットアップ

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

$ yarn global add @nestjs/cli
# プロジェクトを作る
$ nest new nestjs-prisma-crud

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

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

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

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

$ 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を使うためのライブラリです、今回は使わないので入れなくても良いです!

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

ライブラリの設定を行う

prismaとDBのセットアップ

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

$ 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ファイルを使用する事ができるようになります

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とでもつけておきましょう(初回なので)

$ 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

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

$ yarn prisma generate

REST APIでCRUDを作成する

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

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

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

$ 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

作成されるファイル

以下のようなファイルが生成されます
controllermoduleserviceの他にもいくつかファイルができてますね
簡単に説明すると
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
$ 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公式をみてもらえると!

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の方を実装していきます!
実装にはこちらのリポジトリが参考になりました!

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にリクエストを投げてレスポンスを確認してみます!

$ yarn start:dev
  • task Post()
$ curl -X POST localhost:3000/task -H 'Content-Type:application/json' -d "{\"title\":\"あいうえお\", \"content\": \"アイウエオ\"}"

スクリーンショット 2021-03-04 21.51.54.png

  • task Get()
$ curl -X GET localhost:3000/task
[
  {"id":1,"title":"あいうえお","content":"アイウエオ"},
  {"id":2,"title":"かきくけこ","content":"カキクケコ"},
  {"id":3,"title":"さしすせそ","content":"サシスセソ"}
]
  • task Get(':id')
$ curl -X GET localhost:3000/task/1
{"id":1,"title":"あいうえお","content":"アイウエオ"}
  • task Put(':id')
$ curl -X PUT localhost:3000/task/1 -H 'Content-Type:application/json' -d "{\"title\":\"あああああ\", \"content\": \"いいいいい\"}"
{"id":1,"title":"あああああ","content":"いいいいい"}
  • task Delete(':id')
$ curl -X DELETE localhost:3000/task/1
{"id":1,"title":"あああああ","content":"いいいいい"}

gitリポジトリ

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

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

26
26
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
26
26