【入門】Nestjs PrismaでCRUD機能をハンズオン
nestjs prismaでcrud機能を作成
nestjs 最小構成でのハンズオン
今回はnestjsのハンズオンをします。 今回はセットアップから簡単なapiの作成 swaggerでの確認の仕方まで解説します。
目次
nestjsとは
- node.jsのサーバサイドアプリケーションフレームワーク
- TypeScript対応
- 内部的にはExpressかFastifyを使用できる。
セットアップ
nestjs cliをインストールして、プロジェクトを作成します。
$ npm i -g @nestjs/cli
$ nest new project-name
ディレクトリ構造は以下のような感じです。
src
├── app.controller.spec.ts
├── app.controller.ts
├── app.module.ts
├── app.service.ts
└── main.ts
- app.controller.ts
- ルートが 1つの基本的なコントローラー
- app.controller.spec.ts
- コントローラーの単体テスト
- app.module.ts
- アプリケーションのルートモジュール
- app.service.ts
- 単一メソッドの基本サービス
- main.ts
- コア関数NestFactoryを使用して、Nestアプリケーションインスタンスを作成するアプリケーションのエントリファイル
アプリケーションの起動
アプリケーションを起動します。
npm run start
ブラウザを開いてhttp://localhost:3000
にアクセスすることで、Hello World!
の文字が表示されます。
ホットリロードモード
アプロケーションのコードファイルを編集すると、そのコードの変更を実行中のアプリケーションに直ちに適応できる機能をホットリロード
と呼ばれます。
nestjsはホットリロード機能をデフォルトで搭載しています。
npm run start:dev
自分は開発中は基本この機能を使用して開発します。
起動
nestjsでは起動にbootstrapという単語を使うそうです、cssフレームワークで有名なbootstrapとは全然関係ありません。
main.ts
をいじることで起動しています。
NestFactoryでアプリを起動しています。
コントローラー
コントローラーはリクエストを処理して、クライアントに返答します。
cliを利用してコントローラーを作成することができます。
nest g controller cats
サンプルコード
[cat.controller.ts]
import { Controller, Get } from '@nestjs/common';
@Controller('cats')
export class CatsController {
@Get()
findAll(): string {
return '猫が沢山いる';
}
}
このようにしてapiを作成していきます。
swagger
apiの開発でswaggerが推奨されていたので導入しました。
swaggerのインストール
npm install --save @nestjs/swagger
起動
インストールプロセスが完了したら、ファイルを開き、次のクラスSwaggerModuleをmain.tsに使用してSwaggerを初期化します。 このswaggerのインストールを
[main.ts]
import { NestFactory } from '@nestjs/core';
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const config = new DocumentBuilder()
.setTitle('猫たちのAPI例')
.setDescription('猫のAPIドキュメント')
.setVersion('1.0')
.build();
const document = SwaggerModule.createDocument(app, config);
SwaggerModule.setup('api', app, document);
await app.listen(3000);
}
bootstrap();
DocumentBuilderはOpenAPI仕様に準拠したswaggerドキュメントを構築するのに役立ちます。 個人的にはapiを叩く速度が格段に速くなるのが最大のメリットだと感じました。
nestでservice作成
[src/cats/cats.service.ts]
import { Injectable } from '@nestjs/common';
@Injectable()
export class CatService {
async getCats() {
return '猫がとても多くいる';
}
}
http://localhost:3000/api
[src/cats/cats.module.ts]
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
@Module({
controllers: [CatsController],
providers: [CatsService],
})
export class CatsModule {}
[app.module.ts]
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { CatsModule } from './cats/cats.module';
@Module({
imports: [CatsModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
こうすることで、app.module.tsの可読性が向上しますね。
next cliコマンドについて
nest g controller cats
nest g service cats
nest g module cats
ファイルを作成するコマンドですが最低限のソースの記載と、テストコードのジェネレートをする感じですね。 app.module.tsを強制的にいじるので微妙な感じはします 普通にファイルを作る感じでもいいかもです
nest g resource cats
このコマンドはcontrollerとserviceとmoduleの三つの作成がされるので、便利です。
prisma
Prismaは、Node.js,TypeScript用のオープンソースORMです。 nestjsに対応しているormは豊富ですが
prismaのインストール
npm install prisma --save
https://www.prisma.io/docs/concepts/components/prisma-client/crud#read
npx prisma init
このコマンドは以下のことを行います。
- prisma/schema.prisma を作成します
.env
を作成します。
デフォルトではpostgresqlになっていますが、今回は簡単にsqliteで実行します。
[prisma/schema.prisma]
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "sqlite"
url = env("DATABASE_URL")
}
[.env]
DATABASE_URL="file:./dev.db"
実際にcrud処理を作ってみます。 schema.prismaにモデルを追加します
model Cat {
id Int @default(autoincrement()) @id
name String?
}
migrateを走らせます。
npx prisma migrate dev --name init
npx prisma generate
prismaclientのインストール
npm install @prisma/client --save
nestja-prismaを追加する
nest add nestjs-prisma
spliteを選択したら使えるようになります。
PrismaServiceを動かすためにmoduleを修正します
[src/cats/cats.module.ts]
import { Module } from '@nestjs/common';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';
import { PrismaService } from 'nestjs-prisma';
@Module({
controllers: [CatsController],
providers: [CatsService, PrismaService],
})
export class CatsModule {}
[src/cats/cats.service.ts]
import { Injectable } from '@nestjs/common';
import { PrismaService } from 'nestjs-prisma';
@Injectable()
export class CatsService {
constructor(private prisma: PrismaService) {}
async getCats() {
return this.prisma.cat.findFirst();
}
}
データを確認したいですが、初期値がないので簡単にデータを追加できるprisma studioを起動します。
npx prisma studio
確認で来たらprismaが使えるようになりました
crudについて
アプリケーションの基本機能である、
- create 新規作成処理
- read 読み込み処理
- update 更新処理
- delete 削除処理
頭文字4つをとって「CRUD」と呼ばれています。
この4つの機能をprismaを使用してハンズオンしようと思います。
新規作成処理(create)
新規作成処理を作成します createする際に、Request型定義をdtoというものを使って定義します。
DTOについて
- NestJS の DTO は Request Payload(body) の型定義を行うためのもの
- 型定義と同時にバリデーションまで含むことができる
[src/cats/dto/create-cat.dto.ts]
import { ApiProperty } from '@nestjs/swagger';
export class CreateCatDto {
@ApiProperty()
name: string;
}
リクエストのbodyにname: string
を設定しています。
swaggerのドキュメントに追加するために@ApiProperty()
を追加しました
createのcontroller
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { CatsService } from './cats.service';
import { CreateCatDto } from './dto/create-cat.dto';
@Controller('cats')
export class CatsController {
constructor(private readonly catsService: CatsService) {}
@Post()
async create(@Body() createCatDto: CreateCatDto) {
return this.catsService.create(createCatDto);
}
}
-
@Post()
でポストリクエストを受け場合に起動します。 -
constructor(private readonly catsService: CatsService) {}
ここの記述でcatsServiceを注入しています。 -
リクエストにdtoを設定して
-
設定した値をそのままserviceに送っています。
-
処理自体はサービスで行います。
createのservice
import { Injectable } from '@nestjs/common';
import { CreateCatDto } from './dto/create-cat.dto';
import { PrismaService } from 'nestjs-prisma';
@Injectable()
export class CatsService {
constructor(private prisma: PrismaService) {}
async create(createCatDto: CreateCatDto) {
return this.prisma.cat.create({
data: createCatDto,
});
}
}
asyncをしないとprismaの処理を待たずに終了するので、つけました。 今回はリクエストの中身をそのまま送信できるのでdataの中身がdtoになりましたが、変換処理などをおこなう場合はserviceで行えばいいと思います。
読み込み処理(read)
readには一覧を表示する機能と詳細を表示する機能が必要なシーンが多いので基本的なサンプルコードを作成しました。
読み込み処理のcontroller
@Get()
async findAll() {
return this.catsService.findAll();
}
@Get(':id')
async findOne(@Param('id') id: string) {
return this.catsService.findOne(+id);
}
上のメソッドが一覧で下が一件を取り出すメソッドです。
読み込み処理のservice
async findAll() {
return this.prisma.cat.findMany();
}
async findOne(id: number) {
return this.prisma.cat.findUnique({
where: {
id: id,
},
});
}
読み込み処理はprisma.cat.findMany
とprisma.cat.findUnique
ですね。
### 更新処理
猫ちゃんの名前を変更したい場合の処理ですね。
更新処理のdto
[src/cats/dto/update-cat.dto.ts]
import { PartialType } from '@nestjs/swagger';
import { CreateCatDto } from './create-cat.dto';
export class UpdateCatDto extends PartialType(CreateCatDto) {}
更新するにはidとnameが必要ですがcreatedtoの内容を継承してるのでupdateもこのまま更新しようかと思います。
更新処理のcontroller
@Patch(':id')
async update(@Param('id') id: string, @Body() updateCatDto: UpdateCatDto) {
return this.catsService.update(+id, updateCatDto);
}
更新処理のservice
async update(id: number, updateCatDto: UpdateCatDto) {
return this.prisma.cat.update({
where: {
id: id,
},
data: updateCatDto,
});
}
更新処理はprisma.cat.updateですね
このように、idとupdateCatDtoを分けていることで、更新処理もすんなり通せました。
削除処理
削除処理のcontroller
@Delete(':id')
async remove(@Param('id') id: string) {
return this.catsService.remove(+id);
}
削除処理のservice
async remove(id: number) {
return this.prisma.cat.delete({
where: {
id: id,
},
});
}
削除処理は prisma.cat.deleteです。
全体
[ディレクトリ構造]
src/cats
├── cats.controller.ts
├── cats.module.ts
├── cats.service.ts
└── dto
├── create-cat.dto.ts
└── update-cat.dto.ts
[src/cats/cats.controller.ts]
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { CatsService } from './cats.service';
import { CreateCatDto } from './dto/create-cat.dto';
import { UpdateCatDto } from './dto/update-cat.dto';
@Controller('cats')
export class CatsController {
constructor(private readonly catsService: CatsService) {}
@Post()
async create(@Body() createCatDto: CreateCatDto) {
return this.catsService.create(createCatDto);
}
@Get()
async findAll() {
return this.catsService.findAll();
}
@Get(':id')
async findOne(@Param('id') id: string) {
return this.catsService.findOne(+id);
}
@Patch(':id')
async update(@Param('id') id: string, @Body() updateCatDto: UpdateCatDto) {
return this.catsService.update(+id, updateCatDto);
}
@Delete(':id')
async remove(@Param('id') id: string) {
return this.catsService.remove(+id);
}
}
[src/cats/cats.service.ts]
import { Injectable } from '@nestjs/common';
import { CreateCatDto } from './dto/create-cat.dto';
import { UpdateCatDto } from './dto/update-cat.dto';
import { PrismaService } from 'nestjs-prisma';
@Injectable()
export class CatsService {
constructor(private prisma: PrismaService) {}
async create(createCatDto: CreateCatDto) {
return this.prisma.cat.create({
data: createCatDto,
});
}
async findAll() {
return this.prisma.cat.findMany();
}
async findOne(id: number) {
return this.prisma.cat.findUnique({
where: {
id: id,
},
});
}
async update(id: number, updateCatDto: UpdateCatDto) {
return this.prisma.cat.update({
where: {
id: id,
},
data: updateCatDto,
});
}
async remove(id: number) {
return this.prisma.cat.delete({
where: {
id: id,
},
});
}
}
まとめ
nestjsのインストールからprismaを使用して、crud機能の作成までしました
この先にやることは
- ログイン機能
- swaggerのドキュメントをもう少し書く
- バリデーションの実装
などですね。 nodejsでバックエンドの作成ができるようになったのは素晴らしいことだと感じています。