NestJs Prisma 연동(supabase 곁들임)

까망거북·2024년 10월 18일

개인 토이프로젝트로 개발을 진행중 백엔드 엔진으로 NestJs를 사용하기로 했다.
이유는 Spring만 하다가 다른걸 해보고 싶다는 단순한 생각으로.

ORM은 Prisma를 선택하여 연동 방법을 기록으로 남긴다.
참고자료

1. Project 생성

nest new prisma-project ↲
cd prisma-project ↲

2. Prisma 설치

npm i -D prisma ↲
npx prisma ↲ → 설치확인
npx prisma init ↲

  • 프로젝트 최 상단에 prisma 디렉토리 생성 확인

3. 접속 정보 설정

  • supabase에서 프로젝트를 생성한다.

    • 이때 패스워드를 잘 보관한다.
  • supabase -> settings -> Database로 이동을 하면 Connection string을 확인 할수 있다.

  • Mode를 Session으로 변경해야 한다.

    • 변경을 안하며 나중에 마이그레이션이 진행이 안된다.
  • URI항목을 복사한다.

    • [USER-PASSWORD]는 처음 프로젝트 생성시에 만들었다.
  • .env파일에 붙어 녛기 한다.

4. 모델 생성

  • /prisma/schema.prisma 수정

    model Todo {
      id        String   @id @default(uuid())
      createdAt DateTime @default(now())
      updatedAt DateTime @updatedAt
      title     String   @db.VarChar(255)
      content   String
      done      Boolean
    }

    npx prisma migrate dev --name init ↲

    • 만약 오류시 .env파일을 잘 확인 한다.
    • db에 적용 없이 prisma만 수정을 위하면 npx prisma migrate dev --create-only
  • 추가정보

    • npx prisma studio를 하면 DB접속 툴없이 prisma가 지원해주는 툴을 사용할 수 있다.

4.1 schema 분리

모듈별로 schema를 분리 하고자 할때

  • 파일의 경로를 아래와 같은 구조로 한다.
    • schema 디렉토리 아래로 구조를 잡아야 한다.
    /src/prisma
        └/schema
            ├/todos
            │	└todos.prisma
            └schema.prisma
  • schema.prisma를 수정한다.
       generator client {
         provider = "prisma-client-js"
         previewFeatures = ["prismaSchemaFolder"] 
       }
  • todos.prisma수정
    model todos{
      id String @id @default(uuid())
      createdAt DateTime @default(now())
      updatedAt DateTime? @updatedAt
      title String @db.VarChar(255)
      content String?
      done Boolean @default(false)
    }
  • prisma를 업데이트 한다.

    npx prisma migrate dev --name partition ↲

5. CRUD

5.1 클라이언트 설치

npm i @prisma/client ↲
npm i @nestjs/config ↲

5.2 prisma 서비스 생성

  • prisma.module, prisma.service를 생성한다.

    nest g mo prisma ↲
    nest g s prisma ↲

  • prisma.moule 수정

      import { Module, Global } from '@nestjs/common';
      import { PrismaService } from './prisma.service';
    
    + @Global()
      @Module({
        providers: [PrismaService],
    +   exports: [PrismaService],
      })
      export class PrismaModule {}
    • Global을 선언하면 다른 서비스 내에서 import할 필요가 없다.
  • prisma.service 수정

    import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
    import { PrismaClient } from '@prisma/client';
    
    @Injectable()
    export class PrismaService
      extends PrismaClient
      implements OnModuleInit, OnModuleDestroy
    {
      constructor() {
        super({
          datasources: {
            db: {
              url: process.env.DATABASE_URL,
            },
          },
        });
      }
      async onModuleInit() {
        await this.$connect();
      }
    
      async onModuleDestroy() {
        await this.$disconnect();
      }
    
      async enableShutdownHooks(app: INestApplication) {
        process.on('beforeExit', async () => {
          await app.close();
        });
      }
    }
    • onModuleInit : 서버 실행 후 최초 조회를 빠르게 해준다.
    • enableShutdownHooks : 서버 종료 시 진행 중인 DB세션이 있음 세션이 종료 될때 까지 대기한다. main.ts수정 필요
  • main.ts 수정

    import { NestFactory } from '@nestjs/core';
    import { AppModule } from './app.module';
    
    async function bootstrap() {
      const app = await NestFactory.create(AppModule);
      await app.listen(process.env.PORT ?? 3000);
                       
    + const prismaService = app.get(PrismaService);
    + await prismaService.enableShutdownHooks(app);
    }
    bootstrap();
  • app.module수정

  import { Module } from '@nestjs/common';
  import { AppController } from './app.controller';
  import { AppService } from './app.service';
  import { TodosModule } from './todos/todos.module';
+ import { PrismaModule } from './prisma/prisma.module';

  @Module({
    +  imports: [PrismaModule, TodosModule],
       controllers: [AppController],
       providers: [AppService],
  })
  export class AppModule {}

5.3 API 생성

  • crud를 위하여 기본 리소스를 생성한다.

    nest g res todos ↲
    ? What transport layer do you use? REST API ↲
    ? Would you like to generate CRUD entry points? Yes ↲

    src/todos/의 위치에 파일들이 생성된걸 볼수 있다.

    /src/todos
        ├/dto
        │	├create-todo.dto.ts
        │	└update-todo.dto.ts
        ├/entities
        │	└todo.entity.ts
        ├todos.controller.ts
        ├todos.module.ts
        └todos.service.ts

5.4 구현

  • create-todo.dto.ts
export class CreateTodoDto {
    title: string;
    content: string;
}
  • todos.service.ts
import { Injectable } from '@nestjs/common';
import { CreateTodoDto } from './dto/create-todo.dto';
import { UpdateTodoDto } from './dto/update-todo.dto';
import { PrismaService } from 'src/prisma/prisma.service';
import { Prisma } from '@prisma/client';

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

  private readonly model = this.prisma.todos;

  create(data: Prisma.todosCreateInput) {
    return this.model.create({ data });
  }

  findAll() {
    return this.model.findMany();
  }

  findOne(todosWhereUniqueInput: Prisma.todosWhereUniqueInput) {
    return this.model.findUnique({ where: todosWhereUniqueInput });
  }

  update(params: {
    where: Prisma.todosWhereUniqueInput;
    data: Prisma.todosUpdateInput;
  }) {
    return this.model.update(params);
  }

  remove(todosWhereUniqueInput: Prisma.todosWhereUniqueInput) {
    return this.model.delete({ where: todosWhereUniqueInput });
  }
}
  • todos.controller.ts
import {
  Controller,
  Get,
  Post,
  Body,
  Patch,
  Param,
  Delete,
} from '@nestjs/common';
import { TodosService } from './todos.service';
import { CreateTodoDto } from './dto/create-todo.dto';
import { UpdateTodoDto } from './dto/update-todo.dto';

@Controller('todos')
export class TodosController {
  constructor(private readonly todosService: TodosService) {}

  @Post()
  create(@Body() createTodoDto: CreateTodoDto) {
    return this.todosService.create(createTodoDto);
  }

  @Get()
  findAll() {
    return this.todosService.findAll();
  }

  @Get(':id')
  findOne(@Param('id') id: string) {
    return this.todosService.findOne({ id: id });
  }

  @Patch(':id')
  update(@Param('id') id: string, @Body() updateTodoDto: UpdateTodoDto) {
    return this.todosService.update({ where: { id: id }, data: updateTodoDto });
  }

  @Delete(':id')
  remove(@Param('id') id: string) {
    return this.todosService.remove({ id: id });
  }
}

0개의 댓글