nestjs 学习 18:prisma 通关

Prisma 是面向 Node.js/TypeScript 的下一代 ORM(对象关系映射)工具,核心是类型安全、声明式建模、自动迁移、可视化管理,彻底告别手写 SQL 易错、传统 ORM 复杂映射的痛点。

Prisma 由 3 大核心模块构成,分工明确:

  • Prisma Schemaschema.prisma 声明式文件,统一管理数据库连接、数据模型、生成器配置。

  • Prisma Client :自动生成的类型安全查询客户端,提供完整 CRUD API,IDE 自动补全、编译期校验。

  • Prisma Migrate:数据库迁移工具,版本化管理表结构变更,支持回滚、增量同步。

  • Prisma Studio:内置 Web 可视化工具,直接查看 / 编辑数据库数据。

下面以mysql为例子介绍怎么使用 prisma。

同时,以新项目和老项目来分别介绍。

新启动的项目

对于新项目,参考官方文档(www.prisma.io/docs/prisma...%25E3%2580%2582 "https://www.prisma.io/docs/prisma-orm/quickstart/mysql)%E3%80%82")

首先需要安装:

js 复制代码
pnpm add prisma --save-dev

pnpm add @prisma/client @prisma/adapter-mariadb dotenv
  • prisma: Prisma CLI 工具集,它是命令行工具,只在开发 / 构建 / 部署时用,生产环境不需要。

  • @prisma/client: Prisma ORM 的自动生成的、类型安全的数据库客户端。它是你业务代码里唯一用来和数据库交互的工具

它会根据你的 schema.prisma 自动生成 CRUD API。

当你改动schema.prisma文件后:

js 复制代码
model User {
  id    Int
  name  String
  email String
}

然后执行 prisma generate就会自动生成 api:

js 复制代码
prisma.user.findMany()
prisma.user.findUnique()
prisma.user.create()
prisma.user.update()
prisma.user.delete()
  • @prisma/adapter-mariadb:它是 Prisma Client 与 MySQL/MariaDB 之间的官方的翻译层 + 驱动桥接。它让 Prisma 不再内置数据库驱动,而是对接 JS 生态标准驱动(mariadb),Prisma Client 7.x 必须配适配器才能连数据库(强制)。

那什么是数据库驱动呢?

你的代码(Node.js)根本不懂 MySQL 协议 ,MySQL 也听不懂 Node.js 的语言

必须有一个中间件:

  • 把你的指令 → 翻译成 MySQL 能懂的二进制协议
  • 把 MySQL 返回的数据 → 翻译成 JS 对象给你

这个中间件,就叫 驱动(Driver)

接下来看怎么使用。

首先执行:

js 复制代码
pnpm dlx prisma init --datasource-provider mysql --output ../generated/prisma

会创建三个文件:

  • prisma/schema.prisma
js 复制代码
generator client {
  provider = "prisma-client"
  output   = "../generated/prisma"
}

datasource db {
  provider = "mysql"
}
  • .env
js 复制代码
// 把username和password替换为真实的
DATABASE_URL="mysql://username:password@localhost:3306/mydb"
// 自己手动添加
DATABASE_USER="username"
DATABASE_PASSWORD="password"
DATABASE_NAME="mydb"
DATABASE_HOST="localhost"
DATABASE_PORT=3306
  • prisma.config.ts:
js 复制代码
import "dotenv/config";
import { defineConfig, env } from "prisma/config";

export default defineConfig({
  schema: "prisma/schema.prisma",
  migrations: {
    path: "prisma/migrations",
  },
  datasource: {
    url: env("DATABASE_URL"),
  },
});

接下来定义数据模型:

js 复制代码
generator client {
  provider = "prisma-client"
  output   = "../generated/prisma"
}

datasource db {
  provider = "mysql"
}

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

model Post { 
  id        Int     @id @default(autoincrement()) 
  title     String
  content   String? @db.Text
  published Boolean @default(false) 
  author    User    @relation(fields: [authorId], references: [id]) 
  authorId  Int
} 

接着执行:

js 复制代码
pnpm dlx prisma migrate dev --name init

它依次会做如下事情:

  1. 对比新旧模型

Prisma 会对比:

  • 你现在的 schema.prisma
  • 你当前数据库的表结构

找出差异:加了什么表?加了什么字段?改了什么类型?

  1. 生成一个迁移文件(.sql)

prisma/migrations/ 里生成一个文件夹,里面有:

  • migration.sql (真正的 MySQL 建表 / 改表 SQL)
  • 记录版本、时间、名字

这个文件以后可以在生产环境直接执行

  1. 在本地 MySQL 执行这个 SQL

真正去修改你的本地库:

  • 新建表
  • 添加字段
  • 删除字段
  • 修改字段类型
  • 添加索引 / 外键

这一步让你的本地库结构和 schema 保持一致。

  1. 记录迁移历史 在 MySQL 里创建一张 _prisma_migrations 表,记录:
  • 哪些迁移已经执行
  • 什么时候执行的
  • 防止重复执行

最后执行:

js 复制代码
pnpm dlx prisma generate

你写的 schema.prisma 是模型图纸。prisma generate 做的事:拿着你的图纸 → 生成可以直接在代码里用的。

  • prisma.user.findMany()
  • prisma.user.create()
  • prisma.post.update()
  • 所有 TS 类型提示
  • 所有自动补全
  • 所有数据库方法

生成的内容全部在这里generated/prisma/client

为什么你必须手动执行它?

因为你修改了 schema.prisma,类型和方法不会自动更新,必须执行 prisma generate 重新生成。

否则,你加的新字段 VS Code 不提示 ,你加的新表 代码里找不到 prisma.xxx ,TS 报错 找不到字段 / 类型

现在客户端代码生成好了,那么就实例化一个client 去具体的执行增删改查。

创建lib/prisma.ts

js 复制代码
import "dotenv/config";
import { PrismaMariaDb } from "@prisma/adapter-mariadb";
import { PrismaClient } from "../generated/prisma/client";

const adapter = new PrismaMariaDb({
  host: process.env.DATABASE_HOST,
  user: process.env.DATABASE_USER,
  password: process.env.DATABASE_PASSWORD,
  database: process.env.DATABASE_NAME,
  connectionLimit: 5,
});
const prisma = new PrismaClient({ adapter });

export { prisma };

创建一个脚本:

js 复制代码
import { prisma } from "./lib/prisma";

async function main() {
  const user = await prisma.user.create({
    data: {
      name: "Alice",
      email: "alice@prisma.io",
      posts: {
        create: {
          title: "Hello World",
          content: "This is my first post!",
          published: true,
        },
      },
    },
    include: {
      posts: true,
    },
  });
  console.log("Created user:", user);

  // Fetch all users with their posts
  const allUsers = await prisma.user.findMany({
    include: {
      posts: true,
    },
  });
  console.log("All users:", JSON.stringify(allUsers, null, 2));
}

main()
  .then(async () => {
    await prisma.$disconnect();
  })
  .catch(async (e) => {
    console.error(e);
    await prisma.$disconnect();
    process.exit(1);
  });

执行pnpm dlx tsx script.ts就往数据库插入数据了。

总结下流程:

  • 修改schema.prisma,对表结构进行修改;
  • 执行pnpm dlx prisma migrate dev --name add_user_table,把修改同步到数据库;
  • 执行 pnpm dlx prisma generate,对修改的内容生成最新的可执行的代码api;

到这里,开发环境算是完成了,那么现在要上线,就需要把数据库表结构在生产上同步一份。

怎么同步呢?

  1. prisma/migrations 提交到 Git

这个文件夹是结构迁移的核心,里面保存了所有版本的表结构。

生产环境必须拿到这个文件夹才能同步表结构。

  1. 生产环境配置数据库连接

在生产环境的 .env 里写生产 MySQL 地址:

js 复制代码
DATABASE_URL="mysql://user:pass@localhost:3306/production_db"
  1. 生产环境执行一条命令:同步表结构
js 复制代码
pnpm dlx prisma migrate deploy

这条命令做什么?

  • 只同步表结构
  • 不会创建 / 删除 / 修改任何业务数据
  • 不会导入测试数据
  • 不会删除表
  • 不会清空数据
  • 只会执行还没在生产库执行过的 migrations 文件
  • 安全、只读结构、不碰数据

如果有些种子数据需要导入进去,怎么办呢?

使用prisma db seed即可。

  1. 创建种子数据的脚本prisma/seed.ts
js 复制代码
import { prisma } from './prismaClient'

async function main() {
  // 1. 创建默认角色
  await prisma.role.upsert({
    where: { name: 'USER' },
    update: {},
    create: {
      name: 'USER',
      desc: '普通用户'
    },
  })

  // 2. 创建管理员
  await prisma.user.upsert({
    where: { email: 'admin@example.com' },
    update: {},
    create: {
      email: 'admin@example.com',
      name: 'Admin',
      password: 'xxx', // 生产环境请加密
    },
  })

  console.log('✅ 种子数据插入完成')
}

main()
  .catch((e) => console.error(e))
  .finally(async () => await prisma.$disconnect())
  1. prisma.config.ts 注册种子命令 seed: 'tsx prisma/seed.ts'
js 复制代码
import 'dotenv/config'
import { defineConfig } from 'prisma/config'

export default defineConfig({
  schema: 'prisma/schema.prisma',
  migrations: {
    path: 'prisma/migrations',
    seed: 'tsx prisma/seed.ts',
  },
  datasource: {
    url: process.env['DATABASE_URL'],
  },
})
  1. 执行npx prisma db seed就完成了种子数据的插入

老项目

如果数据库已经存在,并且有业务数据,后端项目没有使用Prisma,现在改用 Prisma,该怎么做呢?

  1. 先初始化 Prisma
js 复制代码
pnpm dlx prisma init --datasource-provider mysql
  1. 修改 .env 连接现有数据库
js 复制代码
DATABASE_URL="mysql://用户名:密码@localhost:3306/你的已有数据库名"
  1. 从现有数据库反向生成 schema.prisma
js 复制代码
pnpm dlx prisma db pull

它会做什么?

  • 读取你数据库里所有表
  • 自动生成 model
  • 自动识别字段类型(int/varchar/datetime 等)
  • 自动识别主键、外键、唯一键
  • 自动生成完整的 schema.prisma
  • 完全不碰你的数据,不删不改任何表
  1. 生成客户端代码
js 复制代码
pnpm dlx prisma generate

现在你可以直接用 Prisma 操作老数据库了:

js 复制代码
prisma.你的表名.findMany() 
prisma.你的表名.create()
  1. 我修改了 schema.prisma,怎么办?

这个时候就不能执行pnpm dlx prisma generate了,执行会报:表已经存在 → 试图创建 → 报 Table already exists 错误,它只适用于全新的项目。

需要执行:

js 复制代码
pnpm dlx prisma db push 
pnpm dlx prisma generate

如果我就是想用migrate dev(带版本化迁移)呢?

这是 Prisma 官方高级流程,叫 基线迁移(Baseline) 。官方文档:www.prisma.io/docs/orm/pr...

必须满足两个条件:

  • 在开始使用 Prisma Migrate 之前就已存在的数据库
  • 包含必须保留的数据(如生产环境),即数据库不能被重置

基线化就是通过告诉 Prisma Migrate 假装初始迁移已经被应用,也就是当前所有表已经存在,不要重新创建,从现在开始记录变更,这可以防止生成的迁移在尝试创建已存在的表和字段时失败。

如何创建基线迁移?

第一步: 如果已有 prisma/migrations 文件夹,请删除该文件夹,然后创建新的目录:

js 复制代码
mkdir -p prisma/migrations/0_init

第二步: 使用 prisma migrate diff 生成迁移文件(注意目录前缀使用 0_,以确保 Prisma Migrate 按字典顺序应用迁移):

js 复制代码
npx prisma migrate diff \
  --from-empty \
  --to-schema prisma/schema.prisma \
  --script > prisma/migrations/0_init/migration.sql

第三步: 审查生成的迁移文件,然后运行以下命令将其标记为已应用:

js 复制代码
npx prisma migrate resolve --applied 0_init

此命令会将目标迁移添加到 _prisma_migrations 表并标记为已应用。之后当您运行 prisma migrate deploy 时,Prisma Migrate 将:

  1. 跳过所有标记为"已应用"的迁移(包括基线迁移)
  2. 应用基线迁移之后的所有新迁移

现在你可以正常使用:

js 复制代码
pnpm dlx prisma migrate dev --name xxx

prisma 应用在 nestjs中

首先还是安装必要的库,接着进行初始化:

js 复制代码
npx prisma init

改下 .env 的配置,然后创建 model。

然后创建新的 migration:

js 复制代码
npx prisma migrate dev --name init

这时候数据库就就有这两个表了。

接着执行:

js 复制代码
npx prisma generate

生成 client 代码,接下来我们就可以直接来做 CRUD 了。

现在,创建个 Service:

js 复制代码
nest g service prisma --flat --no-spec

import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import { PrismaMariaDb } from '@prisma/adapter-mariadb';
import { PrismaClient } from '../generated/prisma/client';
import 'dotenv/config';

@Injectable()
export class PrismaService
  extends PrismaClient
  implements OnModuleInit, OnModuleDestroy
{
  constructor() {
    // 1. 创建 Prisma 7.x 必须的 adapter
    const adapter = new PrismaMariaDb({
      host: process.env.DATABASE_HOST as string,
      user: process.env.DATABASE_USER as string,
      password: process.env.DATABASE_PASSWORD as string,
      database: process.env.DATABASE_NAME as string,
      connectionLimit: 5,
    });
    super({
      adapter,
      log: [
        {
          emit: 'stdout',
          level: 'query',
        },
      ],
    });
  }

  async onModuleInit() {
    await this.$connect();
  }

  async onModuleDestroy() {
    await this.$disconnect();
  }
}

然后再创建一个 service,这个 service 里注入 PrismaService,不就可以 CRUD 了么?

js 复制代码
nest g service department --flat --no-spec

import { Inject, Injectable } from '@nestjs/common';
import { PrismaService } from './prisma.service';
import { Prisma } from '../generated/prisma/client';

@Injectable()
export class EmployeeService {
  @Inject(PrismaService)
  private prisma!: PrismaService;

  async create(data: Prisma.EmployeeCreateInput) {
    return await this.prisma.employee.create({
      data,
      select: {
        id: true,
      },
    });
  }
}

插入数据之后,再把 id 查询出来返回。

这里的 data 的 ts 类型不用自己定义,生成的 client 代码里有。

输入 Prisma.Deparment 就会提示出来。

然后在 AppController 里注入这个 service 就可以使用了。

prisma studio

最后介绍下prisma studio这个好用的可视化工具,执行后就可以直接增删改查。

相关推荐
不总是8 小时前
Windows 系统 Node.js 免安装版(zip)安装与配置教程(2026 最新)
前端·windows·node.js
蓝乐15 小时前
Express 知识点总结
node.js·express
kylinmin17 小时前
Node.js安装及环境配置超详细教程(以win11为例子)
node.js
阿奇__18 小时前
基于 Node.js 与智谱 AI 的 RAG 工程实践
人工智能·node.js
hacker_LeeFei19 小时前
前端辨识:npx、npm 、pnpm的区别
前端·npm·node.js
草明20 小时前
检查 node.js 项目中的漏洞
node.js·漏洞·audit
前端程序猿i21 小时前
PM2 使用详解:Node.js 项目后台运行、守护进程、日志与生产部署
node.js·pm2
m0_535817551 天前
从0到1上手Claude Code:Windows安装+88api配置全流程
windows·gpt·node.js·claude·claudecode·88api
大家的林语冰1 天前
Deno 2.8 正式发布,再次超越 Bun,史上最大的次版本升级诞生!
前端·javascript·node.js
hdsoft_huge2 天前
全开源数字孪生系统搭建方案:全套技术文档
vue.js·开源·node.js·echarts·webstorm