nestjs prismaを利用する
prisma v5に伴い記事が古いです。
main.tsとprisma serviceは無価値くらいのレベルなので公式を見に行くか、prisma v5のdocumentを見に行ってください。
基本はnestjsの公式にガイドがあります。ref
1. pnpmを利用してnestjsの環境を構築する
pnpmを利用してnestjsの環境を構築していきます。
directory構成は、以下になることを想定しています。
% tree . -L 3 -I ndoe_modules
.
├── README.md
├── apps
│ └── nestjs-prisma
├── package.json
├── pnpm-lock.yaml
└── pnpm-workspace.yaml
1-1. pnpmの開発ディレクトリ作成とwork spaceの設定
pnpmの開発ディレクトリ作成とwork spaceの設定を実施していきす。
% pnpm init
% touch pnpm-workspace.yaml
% mkdir apps
% cd apps
% pnpm nest new nestjs-prisma -p pnpm
% vi pnpm-workspace.yaml
packages:
- "apps/nestjs-prisma"
rootでapi serverの機能を実施したいため、package.jsonにscriptを追記します。
"serve:dev": "pnpm --parallel --filter \"./apps/**\" start:dev"
1-2. nestjsの起動
% pwd
YOUR_WORKING_DIRECTORY
% pnpm serve:dev
2. prismaと連携するdatabaseを作成していきます。
今回の章完了後にディレクトリ構成は以下になることを想定しています。
% tree . -L 3 -I node_modules
.
├── README.md
├── apps
│ └── nestjs-prisma
├── database
│ └── mysql.dockerfile
├── docker-compose.yaml
├── package.json
├── pnpm-lock.yaml
└── pnpm-workspace.yaml
docker-compose.yaml ファイルと接続するdatabase(mysql)を作成していきます。
adminterなどについは追加していません。理由は、prisma studioでdatabaseの内容が確認できるためです。
% touch docker-compose.yaml
% mkdir database
% touch database/mysql.dockerfile
FROM mysql:8
version: "3"
services:
db:
container_name: db_server
build:
context: ./database
dockerfile: mysql.dockerfile
tty: true
restart: always
ports:
- 3306:3306
volumes:
- db_volume:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: sample
networks:
local: {}
volumes:
db_volume: {}
dockerの記載が完了したら、dockerを起動していきます。
% docker compose up
3. migrationファイルを作成する
prismaのmigrationコマンドを利用して、databaseのmigrationファイルを作成していきます。
この章を完了した後のディレクトリは以下になることを想定しています。
% tree . -L 3 -I node_modules
.
├── README.md
├── apps
│ └── nestjs-prisma
│ ├── README.md
│ ├── dist
│ ├── nest-cli.json
│ ├── package.json
│ ├── .env
│ ├── prisma
│ │ ├── migrations
│ │ ├── schema.prisma
│ ├── src
│ ├── test
│ ├── tsconfig.build.json
│ └── tsconfig.json
├── database
│ └── mysql.dockerfile
├── docker-compose.yaml
├── package.json
├── pnpm-lock.yaml
└── pnpm-workspace.yaml
3-1. prismaの環境を構築する
% pnpm install -D prisma -F nestjs-prisma
% cd apps/nestjs-prisma
% pnpm prisma init --datasource-provider mysql
作成された.envファイルをdockerのmysql向けに変更します。
DATABASE_URL="mysql://root:password@localhost:3306/sample"
3-2. migrationの作成と実施
prismaを利用してmigrationファイルの作成と実施をしていきます。
% pwd
YOUR_WORKING_DIRECTORY/apps/nestjs-prisma
% vi prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "mysql"
url = env("DATABASE_URL")
}
model User {
userUUID String @unique @db.VarChar(36) @id
email String @unique @db.VarChar(255)
name String? @db.VarChar(100)
posts Post[]
createdAt DateTime @db.Timestamp()
updatedAt DateTime @db.Timestamp()
}
model Post {
postUUID String @unique @id @db.VarChar(36)
title String @db.VarChar(100)
content String? @db.Text
published Boolean @default(false)
author User? @relation(fields: [authorId], references: [userUUID])
authorId String? @db.VarChar(36)
createdAt DateTime @db.Timestamp()
updatedAt DateTime @db.Timestamp()
@@index([authorId])
}
3-3. migrationファイルの作成と実施
% pwd
YOUR_WORKING_DIRECTORY/apps/nestjs-prisma
% pnpm prisma migrate dev --name create_user_and_post
実施が完了したら、prsima studio経由で確認を実施してみます。
% pwd
YOUR_WORKING_DIRECTORY/apps/nestjs-prisma
% pnpm prisma studio
localhost 5555にアクセスをして、作成されたModelがあるか確認します。
4. 開発環境向けにSeedファイルを作成する
4-1. seedを実施するための前準備
% pwd
YOUR_WORKING_DIRECTORY/apps/nestjs-prisma
% pnpm install @faker-js/faker -D
% mkdir seeds
% touch seeds/main.ts && touch seeds/create-user-and-post.ts && touch seeds/client.ts
% vi package.json
"prisma": {
"seed": "ts-node ./prisma/seeds/main.ts"
},
import { PrismaClient } from '@prisma/client';
export const prisma = new PrismaClient();
import { prisma } from './client';
import { upsertUsers } from './create-user-and-post';
async function main() {
await upsertUsers();
}
main()
.then(async () => {
await prisma.$disconnect();
})
.catch(async (e) => {
console.error(e);
await prisma.$disconnect();
process.exit(1);
});
import { prisma } from './client';
import { faker } from '@faker-js/faker';
import { Prisma } from '@prisma/client';
import { randomUUID } from 'crypto';
export async function upsertUsers() {
return await Promise.all(
[...Array(2)].map(() => {
const email = faker.internet.email();
return prisma.user.upsert({
where: { email },
update: {},
create: buildCreateUserInput({ email }),
});
}),
);
}
function buildCreateUserInput(input?: Partial<Prisma.UserCreateInput>) {
const email = faker.internet.email();
const name = faker.person.fullName();
const uuid = randomUUID();
return {
userUUID: uuid,
email,
name,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
posts: {
createMany: { data: [buildCreatePostInput()] },
},
...input,
};
}
function buildCreatePostInput(input?: Partial<Prisma.PostCreateInput>) {
const uuid = randomUUID();
const title = faker.lorem.word({ length: { max: 100, min: 1 } });
const content = faker.lorem.word({ length: { max: 190, min: 1 } });
return {
postUUID: uuid,
title,
content,
published: true,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
...input,
};
}
4-2. seedを実行していく
% pwd
YOUR_WORKING_DIRECTORY/apps/nestjs-prisma
% pnpm prisma db seed
一応完了したかを確認するため、localhost 5555にアクセスをして確認します。
5. prismaとnestjsを接続する
最後にprismaとnestjsの接続を実施していきます。
% pwd
YOUR_WORKING_DIRECTORY/apps/nestjs-prisma
% pnpm nest g module shared
% pnpm nest g service shared/services/prisma --flat
import { INestApplication, Injectable, OnModuleInit } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';
@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit() {
await this.$connect();
}
async enableShutdownHooks(app: INestApplication) {
this.$on('beforeExit', async () => {
await app.close();
});
}
}
import { Module } from '@nestjs/common';
import { PrismaService } from './services/prisma.service';
@Module({
providers: [PrismaService],
exports: [PrismaService],
})
export class SharedModule {}
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { PrismaService } from './shared/services/prisma.service';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const prismaService = app.get(PrismaService);
await prismaService.enableShutdownHooks(app);
await app.listen(3000);
}
bootstrap();
import { Controller, Get } from '@nestjs/common';
import { PrismaService } from './shared/services/prisma.service'
@Controller('')
export class UsersController {
@Get('users')
getAllUsers() {
return this.prisma.user.findMany();
}
constructor(private readonly prisma: PrismaService) {}
}
上記で接続ができるか確認を実施して表示できれば、完了です。
typeormの時に存在していたEntity(序盤はtsファイルを読み込めなくてめんどくさかった)をprismaでどのようにあつかうか、angularと同じ場合はserice層が1layerだと辛くなる問題もあると知らないこともあるので、いろいろ試せそう。