⚠️ Alpha内测版本警告:此为早期内部构建版本,尚不完整且可能存在错误,欢迎大家提Issue反馈问题或建议
Skip to content

4.4.6 种子数据:prisma/seed.ts 与 npm run db:seed

一句话破题

种子数据是数据库的"初始内容"——让开发和测试环境拥有可用的示例数据。

配置种子脚本

1. 安装依赖

bash
npm install -D tsx

2. 配置 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/faker
typescript
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 脚本
  • 使用 upsertskipDuplicates 实现幂等
  • 使用 Faker 生成真实的假数据
  • 区分生产和测试环境的种子数据