「项目实战」从0搭建NestJS后端服务(二):数据库选型和Prisma整合

151 阅读4分钟

前言

大家好,我是elk。之前学习NestJS时一直用TypeORM,但最近发现Prisma这个宝藏ORM框架,论坛里都在说它更香!这次就带大家用实战体验一把NestJS+Prisma组合拳,手把手教你们从零搭建用户模块。

(小声BB:官方文档虽然好,但有些坑还得自己踩过才懂)

🗃️ 数据库选型:MySQL vs PostgreSQL

一句话对比指南

  • MySQL:适合快速开发,团队熟悉传统关系型数据库
  • PostgreSQL:适合处理复杂查询、地理位置数据,自带Buff加成

举个栗子🌰:

  • 用户管理系统 → 选MySQL
  • 外卖配送系统(需要地理坐标) → 选PostgreSQL

这次咱们先用MySQL演示,毕竟大多数项目都从这里起步~

Prisma初始化

prisma插件

# 安装核心工具
pnpm install prisma --save-dev

初始化prisma配置

# 初始化配置文件
pnpm prisma init

此时项目根目录创建了一个prisma目录和一个.env文件,里面有一个schema.prisma

  • schema.prisma :指定数据库连接并包含数据库schema
  • .envdotenv文件,通常用于将数据库凭据存储在一组环境变量中

安装prisma客户端

为了能够在Nest应用中与数据库进行交互,需要客户端

# 安装客户端(重要!)
pnpm install @prisma/client

配置连接数据库

  • schema.prisma
// 定义生成器,指定使用 Prisma Client JS 作为生成的客户端代码
generator client {
  // 生成器的提供者,这里使用 Prisma Client JS
  provider = "prisma-client-js"
}
​
// 定义数据源,指定数据库的相关信息
datasource db {
  // 数据源的提供者,这里使用 MySQL 数据库
  provider = "mysql"
  // 数据库连接的 URL,从环境变量中获取
  url      = env("DATABASE_URL")
}
  • .env
# 配置 MySQL 数据库的连接地址,Prisma 将使用该地址连接到数据库
# 数据库的用户名是 root,密码是 123456
# 数据库运行在本地主机上,端口号是 33061
# 要连接的数据库名称是 elk_db
DATABASE_URL="mysql://root:123456@localhost:33061/elk_db"

创建sty_user表

  • Schema.prisma
// 定义生成器,指定使用 Prisma Client JS 作为生成的客户端代码
generator client {
  // 生成器的提供者,这里使用 Prisma Client JS
  provider = "prisma-client-js"
}
​
// 定义数据源,指定数据库的相关信息
datasource db {
  // 数据源的提供者,这里使用 MySQL 数据库
  provider = "mysql"
  // 数据库连接的 URL,从环境变量中获取
  url      = env("DATABASE_URL")
}
​
// 用户表
model sys_user {
  userid    BigInt   @id @default(autoincrement())
  deptid    BigInt
  username  String   @unique
  nickname  String
  email     String   @unique
  phone     String   @unique
  sex       String   @default("0")
  avatar    String
  status    String   @default("0")
  remark    String
  password  String
  createdBy String
  updatedBy String
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
}

pack.json新增命令

"scripts": {
  "prisma:pull-DB": "prisma db pull",
  "prisma:push-DB": "prisma db push",
  "prisma:migrate-create": "prisma migrate dev --name init"
}
  • prisma db pull:用与开发环境中,将数据库的表设定,同步到项目中
  • prisma db push:用于开发环境中,将项目中表定义的改动,同步到数据库中
  • prisma migrate dev --name init:用于开发环境中,将项目中表定义的改动,同步到数据库中,并在项目中生成一条操作记录「数据库迁移,初始化」

执行命令,初始化库

# 在项目终端使用
pnpm prisma:migrate-create

执行完之后,prisma生成一个migrations目录,存放对应的SQL文件

使用vscode中的一个插件 database插件去查看库里是否建立了sty_user表

nest中使用prisma

# 生成专门的module、service文件
# prisma目录下
nest g mo prisma
nest g s prisma
  • prisma.module.ts
import { Module, Global } from '@nestjs/common';
import { PrismaService } from './prisma.service';

@Global()
@Module({
  providers: [PrismaService],
  exports: [PrismaService],
})
export class PrismaModule {}
  • prisma.service.ts
import { INestApplication, Injectable, OnModuleInit } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit {
  async onModuleInit() {
    // 调用 PrismaClient 的 $connect 方法,连接到数据库
    await this.$connect();
  }
  // 关闭连接
  async enableShutdownHooks(app: INestApplication) {
    // 监听 Prisma 客户端的 'beforeExit' 事件
    this.$on('beforeExit', async () => {
      // 调用 NestJS 应用程序的 close 方法,关闭应用程序
      await app.close();
    });
  }
}

user模块调用

# 生成一套CRUD模版
nest g res user
  • user.moduel.ts
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';

// 引入prisma服务
import { PrismaService } from '../../prisma/prisma.service';

@Module({
  controllers: [UserController],
  providers: [UserService, PrismaService],
})
export class UserModule {}
  • user.service.ts
import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { UpdateUserDto } from './dto/update-user.dto';

// 引入prisma服务
import { PrismaService } from '../../prisma/prisma.service';

@Injectable()
export class UserService {
  // 注入prisma服务
  constructor(private prisma: PrismaService) {}
  create(createUserDto: CreateUserDto) {
    return 'This action adds a new user';
  }

  async findAll() {
    // 查询用户表
    const user = await this.prisma.sys_user.findMany();
    console.log('🚀 ~ UserService ~ findAll ~ user:', user);
    return `This action returns all user`;
  }

  findOne(id: number) {
    return `This action returns a #${id} user`;
  }

  update(id: number, updateUserDto: UpdateUserDto) {
    return `This action updates a #${id} user`;
  }

  remove(id: number) {
    return `This action removes a #${id} user`;
  }
}

📍 下期预告

《从0搭建NestJS后端服务(三):环境配置以及CORS处理》
我们将探讨:

  • 如何优雅管理多环境配置
  • 跨域问题的三种解决姿势
  • 接口突然挂了的应急方案

🤝 互动时间

新手村同学:卡在哪一步了?评论区说出你的血泪史!
满级大佬:你们团队怎么处理数据库迁移?求分享最佳实践!

欢迎在评论区分享你的实战经验!