4.4.6 种子数据:prisma/seed.ts 与 npm run db:seed
一句话破题
种子数据是数据库的"初始内容"——让开发和测试环境拥有可用的示例数据。
配置种子脚本
1. 安装依赖:
bash
npm install -D tsx2. 配置 package.json:
json
{
"prisma": {
"seed": "tsx prisma/seed.ts"
}
}3. 创建种子文件:
typescript
// prisma/seed.ts
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
async function main() {
// 创建用户
const user = await prisma.user.upsert({
where: { email: 'admin@example.com' },
update: {},
create: {
email: 'admin@example.com',
name: '管理员',
role: 'ADMIN',
},
})
// 创建文章
await prisma.post.createMany({
data: [
{ title: '第一篇文章', content: '内容...', authorId: user.id },
{ title: '第二篇文章', content: '内容...', authorId: user.id },
],
skipDuplicates: true,
})
console.log('Seed completed!')
}
main()
.catch((e) => {
console.error(e)
process.exit(1)
})
.finally(async () => {
await prisma.$disconnect()
})运行种子脚本
bash
# 手动运行
npx prisma db seed
# 迁移时自动运行(migrate reset 会触发)
npx prisma migrate reset幂等种子设计
使用 upsert 避免重复:
typescript
// 幂等:重复运行不会报错
const user = await prisma.user.upsert({
where: { email: 'admin@example.com' },
update: { name: '管理员' }, // 存在则更新
create: { // 不存在则创建
email: 'admin@example.com',
name: '管理员',
},
})使用 skipDuplicates:
typescript
await prisma.post.createMany({
data: posts,
skipDuplicates: true, // 遇到重复跳过
})使用 Faker 生成假数据
bash
npm install -D @faker-js/fakertypescript
import { faker } from '@faker-js/faker'
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
async function main() {
// 生成 10 个用户
const users = Array.from({ length: 10 }, () => ({
email: faker.internet.email(),
name: faker.person.fullName(),
avatar: faker.image.avatar(),
}))
for (const userData of users) {
const user = await prisma.user.create({ data: userData })
// 每个用户创建 3-5 篇文章
const postCount = faker.number.int({ min: 3, max: 5 })
await prisma.post.createMany({
data: Array.from({ length: postCount }, () => ({
title: faker.lorem.sentence(),
content: faker.lorem.paragraphs(3),
authorId: user.id,
})),
})
}
}
main()分环境种子数据
typescript
// prisma/seed.ts
const isProduction = process.env.NODE_ENV === 'production'
async function main() {
// 所有环境都需要的基础数据
await seedBaseData()
// 仅开发/测试环境
if (!isProduction) {
await seedTestData()
}
}
async function seedBaseData() {
// 角色、权限等基础数据
await prisma.role.upsert({
where: { name: 'ADMIN' },
update: {},
create: { name: 'ADMIN', permissions: ['*'] },
})
}
async function seedTestData() {
// 测试用户、示例数据
// ...
}种子脚本最佳实践
| 实践 | 说明 |
|---|---|
| 幂等设计 | 重复运行不报错、不重复数据 |
| 事务包裹 | 关键数据用事务保证一致性 |
| 分环境 | 区分生产和测试数据 |
| 日志输出 | 输出进度便于调试 |
typescript
async function main() {
console.log('Starting seed...')
await prisma.$transaction(async (tx) => {
console.log('Creating users...')
await seedUsers(tx)
console.log('Creating posts...')
await seedPosts(tx)
})
console.log('Seed completed!')
}清理数据
typescript
async function cleanDatabase() {
// 按照外键依赖顺序删除
await prisma.comment.deleteMany()
await prisma.post.deleteMany()
await prisma.user.deleteMany()
}
async function main() {
await cleanDatabase()
await seedData()
}本节小结
- 在 package.json 配置
prisma.seed脚本 - 使用
upsert或skipDuplicates实现幂等 - 使用 Faker 生成真实的假数据
- 区分生产和测试环境的种子数据
