Prisma 是面向 Node.js/TypeScript 的下一代 ORM(对象关系映射)工具,核心是类型安全、声明式建模、自动迁移、可视化管理,彻底告别手写 SQL 易错、传统 ORM 复杂映射的痛点。
Prisma 由 3 大核心模块构成,分工明确:
-
Prisma Schema :
schema.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
它依次会做如下事情:
- 对比新旧模型
Prisma 会对比:
- 你现在的
schema.prisma - 你当前数据库的表结构
找出差异:加了什么表?加了什么字段?改了什么类型?
- 生成一个迁移文件(.sql)
在 prisma/migrations/ 里生成一个文件夹,里面有:
migration.sql(真正的 MySQL 建表 / 改表 SQL)- 记录版本、时间、名字
这个文件以后可以在生产环境直接执行。

- 在本地 MySQL 执行这个 SQL
真正去修改你的本地库:
- 新建表
- 添加字段
- 删除字段
- 修改字段类型
- 添加索引 / 外键
这一步让你的本地库结构和 schema 保持一致。
- 记录迁移历史 在 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;
到这里,开发环境算是完成了,那么现在要上线,就需要把数据库表结构在生产上同步一份。
怎么同步呢?
- 把
prisma/migrations提交到 Git
这个文件夹是结构迁移的核心,里面保存了所有版本的表结构。
生产环境必须拿到这个文件夹才能同步表结构。
- 生产环境配置数据库连接
在生产环境的 .env 里写生产 MySQL 地址:
js
DATABASE_URL="mysql://user:pass@localhost:3306/production_db"
- 生产环境执行一条命令:同步表结构
js
pnpm dlx prisma migrate deploy
这条命令做什么?
- 只同步表结构
- 不会创建 / 删除 / 修改任何业务数据
- 不会导入测试数据
- 不会删除表
- 不会清空数据
- 只会执行还没在生产库执行过的
migrations文件 - 安全、只读结构、不碰数据
如果有些种子数据需要导入进去,怎么办呢?
使用prisma db seed即可。
- 创建种子数据的脚本
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())
- 在
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'],
},
})
- 执行
npx prisma db seed就完成了种子数据的插入
老项目
如果数据库已经存在,并且有业务数据,后端项目没有使用Prisma,现在改用 Prisma,该怎么做呢?
- 先初始化 Prisma
js
pnpm dlx prisma init --datasource-provider mysql
- 修改
.env连接现有数据库
js
DATABASE_URL="mysql://用户名:密码@localhost:3306/你的已有数据库名"
- 从现有数据库反向生成 schema.prisma
js
pnpm dlx prisma db pull
它会做什么?
- 读取你数据库里所有表
- 自动生成
model - 自动识别字段类型(int/varchar/datetime 等)
- 自动识别主键、外键、唯一键
- 自动生成完整的
schema.prisma - 完全不碰你的数据,不删不改任何表
- 生成客户端代码
js
pnpm dlx prisma generate
现在你可以直接用 Prisma 操作老数据库了:
js
prisma.你的表名.findMany()
prisma.你的表名.create()
- 我修改了 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 将:
- 跳过所有标记为"已应用"的迁移(包括基线迁移)
- 应用基线迁移之后的所有新迁移
现在你可以正常使用:
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这个好用的可视化工具,执行后就可以直接增删改查。
