Skip to main content

What are Seeders?

Seeders are scripts that populate your database with sample or initial data. They’re perfect for setting up test data, default values, or demo content.
Real-world analogy: Seeders are like filling a new garden with plants. After you’ve prepared the soil (migrations), you plant seeds (seeders) to grow your garden with the plants you want.

Creating Seeders

Using the CLI

# Create a seeder
npx ilana make:seeder UserSeeder

# Create seeder when making model
npx ilana make:model Product --seed

Basic Seeder Structure

// database/seeds/UserSeeder.js
const Seeder = require('ilana-orm/orm/Seeder');
const User = require('../../models/User');

class UserSeeder extends Seeder {
  async run() {
    // Clear existing data
    await User.query().delete();
    
    // Insert new data
    await User.create({
      name: 'John Doe',
      email: 'john@example.com',
      password: 'hashed_password',
      is_active: true,
    });
    
    await User.create({
      name: 'Jane Smith',
      email: 'jane@example.com',
      password: 'hashed_password',
      is_active: true,
    });
  }
}

module.exports = UserSeeder;

Running Seeders

Basic Commands

# Run all seeders
npx ilana seed

# Run specific seeder
npx ilana seed --class=UserSeeder

# Run seeders on specific connection
npx ilana seed --connection=mysql

Seeder Patterns

Simple Data Seeding

// CategorySeeder.js
const Seeder = require('ilana-orm/orm/Seeder');
const Category = require('../../models/Category');

class CategorySeeder extends Seeder {
  async run() {
    await Category.query().delete();
    
    const categories = [
      { name: 'Technology', slug: 'technology' },
      { name: 'Health', slug: 'health' },
      { name: 'Travel', slug: 'travel' },
      { name: 'Food', slug: 'food' },
    ];
    
    for (const category of categories) {
      await Category.create(category);
    }
  }
}

module.exports = CategorySeeder;

Using Model Methods

// UserSeeder.js
const Seeder = require('ilana-orm/orm/Seeder');
const User = require('../../models/User');
const bcrypt = require('bcrypt');

class UserSeeder extends Seeder {
  async run() {
    // Clear existing users
    await User.query().delete();
    
    // Create users using model
    await User.create({
      name: 'Admin User',
      email: 'admin@example.com',
      password: await bcrypt.hash('password', 10),
      role: 'admin',
      is_active: true,
    });
    
    await User.create({
      name: 'Regular User',
      email: 'user@example.com',
      password: await bcrypt.hash('password', 10),
      role: 'user',
      is_active: true,
    });
  }
}

module.exports = UserSeeder;

Conditional Seeding

// UserSeeder.js
const Seeder = require('ilana-orm/orm/Seeder');
const User = require('../../models/User');

class UserSeeder extends Seeder {
  async run() {
    // Only seed if no users exist
    const userCount = await User.count();
    
    if (userCount > 0) {
      console.log('Users already exist, skipping seeder');
      return;
    }
    
    // Create users
    await User.factory().times(10).create();
  }
}

module.exports = UserSeeder;

Environment-Specific Seeding

// UserSeeder.js
const Seeder = require('ilana-orm/orm/Seeder');
const User = require('../../models/User');

class UserSeeder extends Seeder {
  async run() {
    await User.query().delete();
    
    if (process.env.NODE_ENV === 'development') {
      // Development data
      await User.create({ name: 'Dev User', email: 'dev@example.com' });
      await User.create({ name: 'Test User', email: 'test@example.com' });
    } else if (process.env.NODE_ENV === 'production') {
      // Production data (admin only)
      await User.create({ name: 'Admin', email: 'admin@company.com', role: 'admin' });
    }
  }
}

module.exports = UserSeeder;

Advanced Seeding Patterns

Seeding with Relationships

// PostSeeder.js
const Seeder = require('ilana-orm/orm/Seeder');
const User = require('../../models/User');
const Post = require('../../models/Post');
const Category = require('../../models/Category');
const { faker } = require('@faker-js/faker');

class PostSeeder extends Seeder {
  async run() {
    await Post.query().delete();
    
    // Get existing users and categories
    const users = await User.all();
    const categories = await Category.all();
    
    if (users.length === 0 || categories.length === 0) {
      console.log('Please run UserSeeder and CategorySeeder first');
      return;
    }
    
    // Create posts for each user
    for (const user of users) {
      await Post.create({
        title: `Sample Post by ${user.name}`,
        content: 'This is sample content for the post.',
        user_id: user.id,
        category_id: faker.helpers.arrayElement(categories).id,
        is_published: true,
      });
    }
  }
}

module.exports = PostSeeder;

Bulk Seeding with Faker

// UserSeeder.js
const Seeder = require('ilana-orm/orm/Seeder');
const User = require('../../models/User');
const { faker } = require('@faker-js/faker');
const bcrypt = require('bcrypt');

class UserSeeder extends Seeder {
  async run() {
    await User.query().delete();
    
    // Generate 100 fake users
    const users = [];
    for (let i = 0; i < 100; i++) {
      users.push({
        name: faker.person.fullName(),
        email: faker.internet.email(),
        password: await bcrypt.hash('password', 10),
        age: faker.number.int({ min: 18, max: 80 }),
        bio: faker.lorem.paragraph(),
        is_active: faker.datatype.boolean(),
      });
    }
    
    // Insert in chunks for better performance
    const chunkSize = 10;
    for (let i = 0; i < users.length; i += chunkSize) {
      const chunk = users.slice(i, i + chunkSize);
      await User.insert(chunk);
    }
  }
}

module.exports = UserSeeder;

Many-to-Many Seeding

// PostTagSeeder.js
const Seeder = require('ilana-orm/orm/Seeder');
const Post = require('../../models/Post');
const Tag = require('../../models/Tag');
const PostTag = require('../../models/PostTag');

class PostTagSeeder extends Seeder {
  async run() {
    await PostTag.query().delete();
    
    const posts = await Post.select('id').get();
    const tags = await Tag.select('id').get();
    
    const postTags = [];
    
    // Assign random tags to each post
    for (const post of posts) {
      const numTags = Math.floor(Math.random() * 3) + 1; // 1-3 tags per post
      const shuffledTags = tags.sort(() => 0.5 - Math.random());
      
      for (let i = 0; i < numTags; i++) {
        postTags.push({
          post_id: post.id,
          tag_id: shuffledTags[i].id,
        });
      }
    }
    
    await PostTag.insert(postTags);
  }
}

module.exports = PostTagSeeder;

Seeder Dependencies

Ordered Seeding

Create a master seeder to control order:
// database/seeds/DatabaseSeeder.js
const Seeder = require('ilana-orm/orm/Seeder');
const UserSeeder = require('./UserSeeder');
const CategorySeeder = require('./CategorySeeder');
const PostSeeder = require('./PostSeeder');
const TagSeeder = require('./TagSeeder');
const PostTagSeeder = require('./PostTagSeeder');

class DatabaseSeeder extends Seeder {
  async run() {
    // Run seeders in order
    await new UserSeeder().run();
    await new CategorySeeder().run();
    await new TagSeeder().run();
    await new PostSeeder().run();
    await new PostTagSeeder().run();
    
    console.log('All seeders completed!');
  }
}

module.exports = DatabaseSeeder;

Reference Data Seeding

// RoleSeeder.js - Run this first
const Seeder = require('ilana-orm/orm/Seeder');
const Role = require('../../models/Role');

class RoleSeeder extends Seeder {
  async run() {
    await Role.query().delete();
    
    const roles = await Role.insert([
      { name: 'admin', description: 'Administrator' },
      { name: 'editor', description: 'Content Editor' },
      { name: 'user', description: 'Regular User' },
    ]);
    
    // Store IDs for other seeders
    global.roleIds = {
      admin: roles.find(r => r.name === 'admin').id,
      editor: roles.find(r => r.name === 'editor').id,
      user: roles.find(r => r.name === 'user').id,
    };
  }
}

module.exports = RoleSeeder;

// UserSeeder.js - Run this after RoleSeeder
const Seeder = require('ilana-orm/orm/Seeder');
const User = require('../../models/User');

class UserSeeder extends Seeder {
  async run() {
    await User.query().delete();
    
    await User.insert([
      {
        name: 'Admin User',
        email: 'admin@example.com',
        role_id: global.roleIds.admin,
      },
      {
        name: 'Editor User',
        email: 'editor@example.com',
        role_id: global.roleIds.editor,
      },
    ]);
  }
}

module.exports = UserSeeder;

Production Seeding

Configuration Data

// ConfigSeeder.js
const Seeder = require('ilana-orm/orm/Seeder');
const Setting = require('../../models/Setting');

class ConfigSeeder extends Seeder {
  async run() {
    // Only insert if not exists
    const existing = await Setting.where('key', 'site_name').first();
    
    if (!existing) {
      await Setting.insert([
        { key: 'site_name', value: 'My Application' },
        { key: 'site_description', value: 'Welcome to our application' },
        { key: 'maintenance_mode', value: 'false' },
        { key: 'max_upload_size', value: '10485760' }, // 10MB
      ]);
    }
  }
}

module.exports = ConfigSeeder;

Default Admin User

// AdminSeeder.js
const Seeder = require('ilana-orm/orm/Seeder');
const User = require('../../models/User');
const bcrypt = require('bcrypt');

class AdminSeeder extends Seeder {
  async run() {
    const adminEmail = process.env.ADMIN_EMAIL || 'admin@example.com';
    const adminPassword = process.env.ADMIN_PASSWORD || 'secure_password';
    
    // Check if admin already exists
    const existingAdmin = await User.where('email', adminEmail).first();
    
    if (!existingAdmin) {
      await User.create({
        name: 'System Administrator',
        email: adminEmail,
        password: await bcrypt.hash(adminPassword, 12),
        role: 'admin',
        is_active: true,
        email_verified_at: new Date(),
      });
      
      console.log(`Admin user created: ${adminEmail}`);
    }
  }
}

module.exports = AdminSeeder;

Testing with Seeders

Test Database Seeding

// TestSeeder.js
const Seeder = require('ilana-orm/orm/Seeder');
const User = require('../../models/User');
const Category = require('../../models/Category');
const Post = require('../../models/Post');
const PostTag = require('../../models/PostTag');

class TestSeeder extends Seeder {
  async run() {
    if (process.env.NODE_ENV !== 'test') {
      console.log('TestSeeder only runs in test environment');
      return;
    }
    
    // Clear all tables
    await PostTag.query().delete();
    await Post.query().delete();
    await User.query().delete();
    await Category.query().delete();
    
    // Insert minimal test data
    const users = await User.insert([
      { name: 'Test User 1', email: 'test1@example.com' },
      { name: 'Test User 2', email: 'test2@example.com' },
    ]);
    
    const categories = await Category.insert([
      { name: 'Test Category', slug: 'test-category' },
    ]);
    
    await Post.insert([
      {
        title: 'Test Post',
        content: 'Test content',
        user_id: users[0].id,
        category_id: categories[0].id,
      },
    ]);
  }
}

module.exports = TestSeeder;

Performance Optimization

Batch Inserts

// Efficient bulk seeding
const Seeder = require('ilana-orm/orm/Seeder');
const User = require('../../models/User');

class BulkUserSeeder extends Seeder {
  async run() {
    await User.query().delete();
    
    const batchSize = 1000;
    const totalUsers = 10000;
    
    for (let i = 0; i < totalUsers; i += batchSize) {
      const batch = [];
      
      for (let j = 0; j < batchSize && (i + j) < totalUsers; j++) {
        batch.push({
          name: `User ${i + j + 1}`,
          email: `user${i + j + 1}@example.com`,
        });
      }
      
      await User.insert(batch);
      console.log(`Inserted batch ${Math.floor(i / batchSize) + 1}`);
    }
  }
}

module.exports = BulkUserSeeder;

Transaction Usage

const Seeder = require('ilana-orm/orm/Seeder');
const User = require('../../models/User');
const Post = require('../../models/Post');
const Database = require('ilana-orm/orm/Database');

class TransactionSeeder extends Seeder {
  async run() {
    await Database.transaction(async (trx) => {
      // All operations in single transaction
      await User.query().useTransaction(trx).delete();
      await Post.query().useTransaction(trx).delete();
      
      const users = await User.useTransaction(trx).insert([
        { name: 'User 1', email: 'user1@example.com' },
        { name: 'User 2', email: 'user2@example.com' },
      ]);
      
      await Post.useTransaction(trx).insert([
        { title: 'Post 1', user_id: users[0].id },
        { title: 'Post 2', user_id: users[1].id },
      ]);
    });
  }
}

module.exports = TransactionSeeder;

Best Practices

1. Clear Data First

// ✅ Good - predictable state
class UserSeeder extends Seeder {
  async run() {
    await User.query().delete(); // Clear first
    await User.insert([...]); // Then insert
  }
}

// ❌ Bad - unpredictable state
class UserSeeder extends Seeder {
  async run() {
    await User.insert([...]); // Might create duplicates
  }
}

2. Use Meaningful Data

// ✅ Good - realistic data
await User.insert([
  { name: 'John Doe', email: 'john@example.com', role: 'admin' },
  { name: 'Jane Smith', email: 'jane@example.com', role: 'user' },
]);

// ❌ Bad - meaningless data
await User.insert([
  { name: 'User 1', email: 'user1@test.com' },
  { name: 'User 2', email: 'user2@test.com' },
]);

3. Handle Dependencies

// ✅ Good - check dependencies
class PostSeeder extends Seeder {
  async run() {
    const userCount = await User.count();
    if (userCount === 0) {
      throw new Error('Please run UserSeeder first');
    }
    
    // Continue with seeding...
  }
}

4. Environment Awareness

// ✅ Good - environment specific
class EnvironmentSeeder extends Seeder {
  async run() {
    if (process.env.NODE_ENV === 'production') {
      // Only essential data in production
      await this.seedEssentialData();
    } else {
      // Full demo data in development
      await this.seedDemoData();
    }
  }
  
  async seedEssentialData() {
    // Essential data logic
  }
  
  async seedDemoData() {
    // Demo data logic
  }
}

Troubleshooting

Problem: Cannot insert due to foreign key constraintSolutions:
  • Run seeders in correct order (parent tables first)
  • Check that referenced records exist
  • Use proper foreign key values
Problem: Unique constraint violationSolutions:
  • Clear existing data first: await Model.query().delete()
  • Check for existing records before inserting
  • Use firstOrCreate() or updateOrCreate() for upserts
Problem: Seeder doesn’t executeSolutions:
  • Check seeder file name and location
  • Verify exports.seed function exists
  • Check for syntax errors in seeder file
  • Use --class=SeederName to run specific seeder

Next Steps

Model Factories

Generate realistic test data automatically

Database Connections

Manage multiple database connections