掘金 后端 ( ) • 2024-04-10 15:30

在Nodejs项目的ORM中,Prisma热度越来越高,如果数据库使Postgresql, Mysql等关系型数据库,还是非常建议使用的。Mongo数据库的的有些特性不支持,建议还是使用typegoose或者mongoose.

下面说一下在Nestjs中prisma的使用、打包、部署。

演示代码

Nestjs项目中引入prisma

https://www.prisma.io/docs/getting-started/setup-prisma/add-to-existing-project/relational-databases-typescript-postgresql

假设当前已经有nestjs项目,需要添加Prisma ORM, 参考以上官方文档,执行如下操作

yarn add prisma -D

执行成功后,会在项目根目录下生成prisma目录和.env文件,在这里,我期望prisma的目录放到src目录下,因此在package.json中添加以下配置

  "prisma": {
    "schema": "src/db/postgresql/schema.prisma"
  }

按配置中的路径创建相关目录,并把schma.prisma文件移动到目标目录

mkdir -p src/db/postgresql
mv prisma/schema.prisma src/db/postgresql
rm -rf prisma

修改.env中的数据库连接地址

DATABASE_URL="postgresql://test:[email protected]:5432/prisma-example?schema=public"

添加数据库模型定义

// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init

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

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}


model User {
  id        Int    @id @default(autoincrement())
  name      String
  email     String
  posts     Post[]
  comments  Comment[]
}

model Post {
  id        Int    @id @default(autoincrement())
  title     String
  content   String
  published  Boolean
  authorId  Int
  author    User    @relation(fields: [authorId], references: [id])
  comments  Comment[]
  createdAt DateTime @default(now())
  updatedAt  DateTime @updatedAt
}

model Comment {
  id     Int    @id @default(autoincrement())
  postId Int
  post   Post   @relation(fields: [postId], references: [id])
  userId  Int
  user    User   @relation(fields: [userId], references: [id])
  content  String
  createdAt DateTime @default(now())
  updatedAt  DateTime @updatedAt
}

安装prisma client

yarn add @prisma/client
# 生成prisma js client
yarn prisma generate

执行完yarn prisma generate命令后,会在node_nodules目录下生成.prisma/client目录, 该目录中的内容正是根据前边定义的schema.prisma文件动态生成。当前也可以通过配置,生成到node_nodules之外的目录下面。

例如, 修改schema.prisam文件,指定生成的目标目录,注意,该目标目录的路径是相对schema.prisma文件的路径。

generator client {
  provider = "prisma-client-js"
  output   = ".prisma/client"
}

这里我使用默认生成路径,不添加output配置。

使用定义的数据库模型初始化数据库

❕ 注意,执行以下命令,DATABASE_URL中配置的连接账号必须要有创建数据库的权限。

通常,该命令都是在本地开发环境,连接本地数据库去执行,并不会直接连接部署环境执行。

yarn prisma migrate dev --name init

以上命令执行完成以后,会有如下影响

  1. 生成了迁移脚本

src\db\postgresql目录下,生成了migrations目录,里边包含了将要在数据库执行的脚本文件migration.sql

  1. 在目标数据库中创建了数据库表

查看数据库,会发现user, post, comment表已经创建出来

  1. 在数据库中记录了migration脚本的执行历史记录
    细心的同学会发现,prisma-example数据库中额外生成了 _prisma_migrations, 这张表的作用正是记录脚本执行历史。查看一下表的内容:

连接数据库

  1. 添加nestjs-prisam模块
yarn add nestjs-prisma
  1. app.module文件中引入prisma module
@Module({
  imports: [PrismaModule.forRoot()],
  controllers: [AppController],
  providers: [AppService],
})
  export class AppModule {}
  1. 添加创建用户、查询用户接口

实现业务逻辑

export class AppService {
  constructor(private readonly _prisma: PrismaService) {}

  async createUser() {
    const user = await this._prisma.user.create({
      data: {
        name: 'John Doe',
        email: '[email protected]',
      },
    });

    return user;
  }

  
  async findUser(id: number) {
    return this._prisma.user.findUnique({
      where: {
        id,
      },
    });
  }
}

添加访问路由

@Controller()
export class AppController {
  constructor(private readonly appService: AppService) {}
  
  @Post('user)
  createUser() {
    return this.appService.createUser();
  }

  @Get('user')
  findUser(@Query('id') id: string) {
    return this.appService.findUser(Number(id));
  }
}
  1. 启动服务
yarn start:dev
  1. 请求创建用户接口

服务构建

  1. 添加.dockerignore文件
# docker镜像构建,需要排除的文件及目录
/node_modules
/dist
  1. 创建Dockerfile
FROM node:18.14-slim as builder
LABEL Author="mobiusy"

WORKDIR /build

RUN yarn config set registry https://registry.npmmirror.com

COPY package.json ./package.json
COPY yarn.lock ./yarn.lock

RUN yarn

COPY ./ ./

RUN yarn prisma generate && yarn build

FROM node:18.14-slim as prod

LABEL Author="mobiusy"

# 设置时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime  && echo 'Asia/Shanghai' >/etc/timezone

WORKDIR /opt/application

# 将生成的可执行文件copy到当前工作目录下
COPY --from=builder /build/dist ./dist
COPY --from=builder /build/node_modules ./node_modules
COPY --from=builder /build/package.json ./package.json


# 容器启动时执行的命令
CMD ["yarn", "start:prod"]
  1. 添加docker-compose.yaml文件
version: '3.7'
services:
  wdp5-backend:
    image: "prisma-example:v0.0.1"
    build:
      context: ./
      dockerfile: Dockerfile
    container_name: prisma-example
    restart: always
    env_file:
      - ./.env
    ports:
      - 33100:13000
  1. 构建并启动容器
docker-compose up -d --build
  1. 测试接口

使用pkg构建二进制文件并docker部署

  1. 安装pkg
yarn add pkg -D
  1. 修改pakcage.json文件,增加打包配置
"bin": "dist/main.js",
"pkg": {
  "targets": [
    "node18"
  ],
  "assets": [
    "node_modules/.prisma/**/*"
  ],
  "output": "nestjs-prisma-example"
}
  1. 新增pkg.Dockerfile
FROM node:18.14-slim as builder
LABEL Author="mobiusy"

WORKDIR /build

RUN yarn config set registry https://registry.npmmirror.com

COPY package.json ./package.json
COPY yarn.lock ./yarn.lock

RUN yarn

COPY ./ ./

RUN yarn prisma generate && yarn build
RUN yarn pkg package.json

FROM node:18.14-slim as prod

LABEL Author="mobiusy"

# 设置时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime  && echo 'Asia/Shanghai' >/etc/timezone

WORKDIR /opt/application


# 将生成的可执行文件copy到当前工作目录下
COPY --from=builder /build/nestjs-prisma-example ./


# 容器启动时执行的命令t
CMD ["./nestjs-prisma-example"]
  1. docker-compose.yaml中新增service
version: '3.7'
services:
  wdp5-backend:
    ...

  # 以下为新增内容
  wdp5-backend-pkg:
    image: "prisma-example:v0.0.1-pkg"
    build:
      context: ./
      dockerfile: pkg.Dockerfile
    container_name: prisma-example-pkg
    restart: always
    env_file:
      - ./.env
    ports:
      - 33200:13000
  1. 构建并启动容器
docker-compose up -d --build

⚠️ 这一步会使用pkg打包linux可执行二进制文件,pkg在打包过程中会下载依赖,如果下载出错,请使用科学上网打包,或预下载依赖放到打包环境中, 具体细节这里不展开说了。

  1. 接口测试

结语

这一篇文章讲了nestjs中Prisma ORM的安装,使用,以及源码部署和二级制部署的方式。

对于Prisma的使用,有很多细节的地方,特别是Prisma Cli,建议仔细阅读官方文档。

https://www.prisma.io/docs

下一篇文章里,我会讲一下如何使用Prisma提供的增量脚本功能,以及如何在生产环境中,去保持数据库和服务的一致性。