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

Next Steps