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/faker
1// 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 seed

Test 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#

  1. Make seeds idempotent - Use upsert or skipDuplicates for repeatability
  2. Use environment-specific seeds - Different data for dev/staging/prod
  3. Hash passwords securely - Never store plain text passwords
  4. Clear data before seeding - Start fresh to avoid duplicates
  5. Document test accounts - Make test credentials easy to find