Database Seeding
Patterns for seeding databases with test and initial data.
Overview#
Database seeding enables:
- Consistent development environments
- Test data generation
- Initial production data
- Demo environment setup
Prerequisites:
- Prisma ORM setup
- Faker.js (optional, for realistic data)
Installation#
npm install -D @faker-js/faker1// package.json
2{
3 "prisma": {
4 "seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts"
5 },
6 "scripts": {
7 "db:seed": "prisma db seed",
8 "db:seed:prod": "SEED_ENV=production prisma db seed",
9 "db:reset": "prisma migrate reset"
10 }
11}Implementation#
Basic Seed Script#
1// prisma/seed.ts
2import { prisma } from '../src/lib/db'
3import { hash } from 'bcryptjs'
4
5async function main() {
6 console.log('Seeding database...')
7
8 // Create admin user
9 const adminPassword = await hash('admin123', 12)
10 const admin = await prisma.user.upsert({
11 where: { email: 'admin@example.com' },
12 update: {},
13 create: {
14 email: 'admin@example.com',
15 name: 'Admin User',
16 password: adminPassword,
17 role: 'ADMIN',
18 emailVerified: new Date()
19 }
20 })
21
22 console.log('Created admin:', admin.email)
23
24 // Create categories
25 const categories = await Promise.all(
26 ['Technology', 'Design', 'Business'].map(name =>
27 prisma.category.upsert({
28 where: { slug: name.toLowerCase() },
29 update: {},
30 create: { name, slug: name.toLowerCase() }
31 })
32 )
33 )
34
35 console.log('Created categories:', categories.length)
36
37 // Create sample posts
38 for (let i = 0; i < 10; i++) {
39 await prisma.post.create({
40 data: {
41 title: `Sample Post ${i + 1}`,
42 slug: `sample-post-${i + 1}`,
43 content: `This is the content for post ${i + 1}`,
44 published: true,
45 authorId: admin.id,
46 categoryId: categories[i % categories.length].id
47 }
48 })
49 }
50
51 console.log('Created 10 sample posts')
52}
53
54main()
55 .catch(console.error)
56 .finally(() => prisma.$disconnect())Faker-Based Seeding#
1// prisma/seed.ts
2import { prisma } from '../src/lib/db'
3import { faker } from '@faker-js/faker'
4import { hash } from 'bcryptjs'
5
6async function main() {
7 // Clear existing data
8 await prisma.post.deleteMany()
9 await prisma.user.deleteMany()
10
11 // Create users
12 const password = await hash('password123', 12)
13
14 const users = await Promise.all(
15 Array.from({ length: 20 }).map(() =>
16 prisma.user.create({
17 data: {
18 email: faker.internet.email(),
19 name: faker.person.fullName(),
20 password,
21 image: faker.image.avatar(),
22 emailVerified: faker.datatype.boolean() ? faker.date.past() : null
23 }
24 })
25 )
26 )
27
28 // Create posts
29 const posts = await Promise.all(
30 Array.from({ length: 50 }).map(() =>
31 prisma.post.create({
32 data: {
33 title: faker.lorem.sentence(),
34 slug: faker.helpers.slugify(faker.lorem.words(3)),
35 content: faker.lorem.paragraphs(5),
36 excerpt: faker.lorem.paragraph(),
37 published: faker.datatype.boolean(),
38 authorId: faker.helpers.arrayElement(users).id,
39 createdAt: faker.date.past(),
40 updatedAt: faker.date.recent()
41 }
42 })
43 )
44 )
45
46 // Create comments
47 await Promise.all(
48 posts.flatMap(post =>
49 Array.from({ length: faker.number.int({ min: 0, max: 10 }) }).map(() =>
50 prisma.comment.create({
51 data: {
52 content: faker.lorem.sentences(),
53 postId: post.id,
54 userId: faker.helpers.arrayElement(users).id,
55 createdAt: faker.date.recent()
56 }
57 })
58 )
59 )
60 )
61
62 console.log('Database seeded!')
63}
64
65main()
66 .catch(console.error)
67 .finally(() => prisma.$disconnect())Environment-Specific Seeds#
1// prisma/seed.ts
2import { prisma } from '../src/lib/db'
3
4const ENV = process.env.SEED_ENV || 'development'
5
6async function seedDevelopment() {
7 // Create test users and data
8 await createTestUsers()
9 await createSampleContent()
10}
11
12async function seedProduction() {
13 // Only create essential data
14 await createAdminUser()
15 await createDefaultCategories()
16 await createSystemSettings()
17}
18
19async function seedStaging() {
20 // Production-like but with some test data
21 await seedProduction()
22 await createDemoAccounts()
23}
24
25async function main() {
26 console.log(`Seeding for environment: ${ENV}`)
27
28 switch (ENV) {
29 case 'production':
30 await seedProduction()
31 break
32 case 'staging':
33 await seedStaging()
34 break
35 default:
36 await seedDevelopment()
37 }
38}
39
40main()
41 .catch(console.error)
42 .finally(() => prisma.$disconnect())Seed with Relations#
1// prisma/seed.ts
2async function seedWithRelations() {
3 // Create team with members
4 const team = await prisma.team.create({
5 data: {
6 name: 'Development Team',
7 members: {
8 create: [
9 { role: 'OWNER', user: { create: { email: 'owner@example.com', name: 'Owner' } } },
10 { role: 'ADMIN', user: { create: { email: 'admin@example.com', name: 'Admin' } } },
11 { role: 'MEMBER', user: { create: { email: 'member@example.com', name: 'Member' } } }
12 ]
13 },
14 projects: {
15 create: [
16 {
17 name: 'Project Alpha',
18 tasks: {
19 create: [
20 { title: 'Task 1', status: 'TODO' },
21 { title: 'Task 2', status: 'IN_PROGRESS' },
22 { title: 'Task 3', status: 'DONE' }
23 ]
24 }
25 }
26 ]
27 }
28 },
29 include: {
30 members: { include: { user: true } },
31 projects: { include: { tasks: true } }
32 }
33 })
34
35 console.log('Created team with members and projects:', team.name)
36}Idempotent Seeding#
1// prisma/seed.ts
2async function idempotentSeed() {
3 // Use upsert to make seeding repeatable
4 const categories = [
5 { name: 'Technology', slug: 'technology' },
6 { name: 'Design', slug: 'design' },
7 { name: 'Business', slug: 'business' }
8 ]
9
10 for (const category of categories) {
11 await prisma.category.upsert({
12 where: { slug: category.slug },
13 update: { name: category.name },
14 create: category
15 })
16 }
17
18 // Or use createMany with skipDuplicates
19 await prisma.setting.createMany({
20 data: [
21 { key: 'site_name', value: 'My App' },
22 { key: 'site_description', value: 'A great application' },
23 { key: 'maintenance_mode', value: 'false' }
24 ],
25 skipDuplicates: true
26 })
27}Seed from JSON/CSV#
1// prisma/seed.ts
2import * as fs from 'fs'
3import * as path from 'path'
4
5async function seedFromFile() {
6 // Read JSON file
7 const dataPath = path.join(__dirname, 'data', 'products.json')
8 const products = JSON.parse(fs.readFileSync(dataPath, 'utf-8'))
9
10 // Batch insert
11 await prisma.product.createMany({
12 data: products,
13 skipDuplicates: true
14 })
15
16 console.log(`Seeded ${products.length} products from file`)
17}Usage Examples#
Running Seeds#
1# Run seed
2npx prisma db seed
3
4# Reset and reseed
5npx prisma migrate reset
6
7# Seed specific environment
8SEED_ENV=staging npx prisma db seedTest Setup with Seeds#
1// tests/setup.ts
2import { prisma } from '@/lib/db'
3import { seedTestData } from './fixtures'
4
5beforeAll(async () => {
6 await prisma.$transaction([
7 prisma.post.deleteMany(),
8 prisma.user.deleteMany()
9 ])
10 await seedTestData()
11})
12
13afterAll(async () => {
14 await prisma.$disconnect()
15})Best Practices#
- Make seeds idempotent - Use upsert or skipDuplicates for repeatability
- Use environment-specific seeds - Different data for dev/staging/prod
- Hash passwords securely - Never store plain text passwords
- Clear data before seeding - Start fresh to avoid duplicates
- Document test accounts - Make test credentials easy to find
Related Patterns#
- Prisma - Prisma setup and basics
- Migrations - Migration strategies
- Transactions - Transaction handling