Skip to content

vanjoe667/seedwise

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

7 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🌱 Seedwise

Smart database seeding with run-once tracking, environment awareness, and multi-ORM support.

npm version npm downloads License: MIT


The Problem

Database seeders in most ORMs run every single time. Unlike migrations, there's no tracking of what's already been seeded.

# This runs ALL seeders, even ones that already ran
npx knex seed:run   # 😬 Duplicate admin users? Again?

Seedwise fixes this. It tracks which seeders have run, supports multiple run modes, and gives you full control over your seeding strategy.


Features

  • βœ… Run-once tracking β€” Seeders only run when they haven't before
  • πŸ”„ Multiple run modes β€” once, always, changed, manual
  • 🌍 Environment-aware β€” Different seeds for dev/staging/prod
  • 🏷️ Tags & groups β€” Run subsets of seeds with --tag
  • πŸ”— Dependencies β€” Define seed execution order
  • πŸ”’ Lock mechanism β€” Prevents concurrent seeding
  • πŸ“Š History tracking β€” See what ran, when, and how long it took
  • πŸ”Œ Multi-ORM β€” Knex, Prisma, Sequelize, Drizzle (planned)

Installation

# For Knex/Objection.js users
npm install @seedwise/core @seedwise/knex

# Coming soon:
# npm install @seedwise/core @seedwise/prisma
# npm install @seedwise/core @seedwise/sequelize
# npm install @seedwise/core @seedwise/drizzle

Quick Start

1. Initialize Seedwise

npx seedwise init

This creates:

  • seedwise.config.js β€” Configuration file
  • seeds/ directory (if it doesn't exist)
  • Database tables (_seedwise_history, _seedwise_lock)

2. Create a Seed

npx seedwise create admin-user
// seeds/001_admin_user.ts
import { SeedConfig } from '@seedwise/core';

export const config: SeedConfig = {
  mode: 'once',
  environments: ['development', 'staging', 'production'],
  tags: ['auth', 'critical'],
};

export async function seed(knex) {
  await knex('users').insert({
    email: 'admin@example.com',
    role: 'admin',
    created_at: new Date(),
  });
}

3. Run Seeds

npx seedwise run

Output:

🌱 Seedwise v1.0.0
Environment: development

Running seeds...
  βœ“ 001_admin_user (once) β€” 23ms
  βœ“ 002_default_roles (once) β€” 15ms
  ⊘ 003_test_users (skipped, prod-only)

Done! 2 seeds executed, 1 skipped.

Run Modes

Mode Behavior Use Case
once Runs one time ever (default) Admin users, default roles, initial config
always Runs on every seedwise run Cache warmup, reference data sync
changed Re-runs if file content changed Config that evolves with code
manual Only runs with --force or --name Dangerous operations, data fixes
export const config: SeedConfig = {
  mode: 'always', // This seed runs every time
};

Configuration

seedwise.config.js

module.exports = {
  // Adapter configuration
  adapter: 'knex',
  
  // Path to your knexfile or ORM config
  connection: './knexfile.js',
  
  // Seeds directory
  seedsDir: './seeds',
  
  // Table names (customizable)
  historyTable: '_seedwise_history',
  lockTable: '_seedwise_lock',
  
  // Default timeout for seeds (ms)
  timeout: 30000,
  
  // Lock timeout - force release if held longer (ms)
  lockTimeout: 60000,
  
  // File extensions to look for
  extensions: ['.js', '.ts'],
  
  // Sort order
  sortBy: 'filename', // or 'dependency'
};

Seed Configuration Options

import { SeedConfig } from '@seedwise/core';

export const config: SeedConfig = {
  // Run mode
  mode: 'once' | 'always' | 'changed' | 'manual',
  
  // Environment filtering
  environments: ['development', 'staging'],
  // OR
  excludeEnvironments: ['production'],
  
  // Tags for filtering
  tags: ['auth', 'critical'],
  
  // Dependencies (run these first)
  dependsOn: ['001_roles', '002_permissions'],
  
  // Transaction control
  transaction: true, // default: true
  
  // Timeout override (ms)
  timeout: 60000,
  
  // Mark as idempotent (informational)
  idempotent: true,
};

CLI Reference

seedwise init

Initialize Seedwise in your project.

npx seedwise init
npx seedwise init --adapter prisma

seedwise create <name>

Create a new seed file.

npx seedwise create user-roles
npx seedwise create user-roles --mode always
npx seedwise create user-roles --tags auth,critical

seedwise run

Execute pending seeds.

npx seedwise run                    # Run all pending seeds
npx seedwise run --force            # Re-run all seeds
npx seedwise run --tag auth         # Only seeds tagged 'auth'
npx seedwise run --env production   # Override NODE_ENV
npx seedwise run --dry-run          # Show what would run
npx seedwise run --name 001_admin   # Run specific seed
npx seedwise run --verbose          # Detailed output

seedwise status

Show seed history and pending seeds.

npx seedwise status
npx seedwise status --pending       # Only show pending
npx seedwise status --json          # JSON output

Output:

🌱 Seedwise Status

History:
  βœ“ 001_admin_user      once     2024-01-15 10:23:45  (23ms)
  βœ“ 002_default_roles   once     2024-01-15 10:23:45  (15ms)
  βœ“ 003_cache_warmup    always   2024-01-15 10:23:46  (102ms)

Pending:
  β—― 004_new_feature     once     (not yet run)

seedwise reset

Clear seed history (use with caution).

npx seedwise reset                  # Reset all (requires confirmation)
npx seedwise reset --name 001_admin # Reset specific seed
npx seedwise reset --force          # Skip confirmation

seedwise unlock

Force-release a stale lock.

npx seedwise unlock

Programmatic API

import { Seedwise } from '@seedwise/core';
import { KnexAdapter } from '@seedwise/knex';
import knex from './knexfile';

const seedwise = new Seedwise({
  adapter: new KnexAdapter(knex),
  seedsDir: './seeds',
});

// Run all pending seeds
await seedwise.run();

// Run with options
await seedwise.run({
  tags: ['auth'],
  force: true,
  dryRun: false,
});

// Get status
const status = await seedwise.status();
console.log(status.pending);  // Seeds that need to run
console.log(status.history);  // Seeds that have run

// Reset specific seed
await seedwise.reset('001_admin_user');

Database Tables

Seedwise creates two tables in your database:

_seedwise_history

Column Type Description
id serial Primary key
name varchar(255) Seed filename
batch integer Batch number
mode varchar(50) Run mode used
environment varchar(50) NODE_ENV when run
checksum varchar(64) File hash (for changed mode)
executed_at timestamp Execution timestamp
duration_ms integer Execution time
status varchar(20) success, failed, skipped

_seedwise_lock

Column Type Description
index integer Always 1
is_locked integer 0 or 1
locked_at timestamp When lock was acquired
locked_by varchar(255) Hostname/process ID

Adapters

Knex / Objection.js

npm install @seedwise/core @seedwise/knex
// seedwise.config.js
module.exports = {
  adapter: 'knex',
  connection: './knexfile.js',
};

Prisma (Coming Soon)

npm install @seedwise/core @seedwise/prisma

Sequelize (Coming Soon)

npm install @seedwise/core @seedwise/sequelize

Drizzle (Coming Soon)

npm install @seedwise/core @seedwise/drizzle

Best Practices

1. Use Descriptive Names

βœ“ 001_create_admin_user.ts
βœ“ 002_seed_default_roles.ts
βœ“ 003_populate_countries.ts

βœ— seed1.ts
βœ— data.ts

2. Number Your Seeds

Prefix with numbers to ensure consistent ordering:

001_roles.ts
002_permissions.ts
003_users.ts  # depends on roles

3. Use Dependencies for Complex Ordering

export const config = {
  dependsOn: ['001_roles', '002_permissions'],
};

4. Tag Appropriately

export const config = {
  tags: ['auth'],  // Can run just auth seeds in CI
};

5. Keep Seeds Idempotent When Possible

// Good: Upsert pattern
await knex('roles')
  .insert({ name: 'admin', level: 100 })
  .onConflict('name')
  .merge();

// Risky: Plain insert (fails on re-run)
await knex('roles').insert({ name: 'admin', level: 100 });

6. Use Environment Filtering

export const config = {
  environments: ['development', 'test'],  // Never in prod
};

FAQ

Q: How is this different from Knex's built-in seeding?

Knex seeds run every time with no tracking. Seedwise adds run-once tracking, environment awareness, tags, dependencies, and more.

Q: Can I use this with an existing project?

Yes! Run seedwise init and it will detect your existing seed files. Already-seeded data won't be affected until you run seedwise run.

Q: What happens if seeding fails midway?

By default, each seed runs in a transaction. If it fails, that seed rolls back. The lock is released, and you can fix and re-run.

Q: How do I run seeds in CI/CD?

# GitHub Actions example
- name: Run database seeds
  run: npx seedwise run --env production

Q: Can I use TypeScript?

Yes! Seedwise supports both .js and .ts files out of the box.


Contributing

Contributions are welcome! Please read our Contributing Guide before submitting a PR.

# Clone the repo
git clone https://github.com/vanjoe667/seedwise.git

# Install dependencies
pnpm install

# Run tests
pnpm test

# Build
pnpm build

License

MIT Β© Joel Ajide Ademola


Acknowledgments

Inspired by the migration patterns in Knex, Prisma, and Laravel's seeding system.


Built with ❀️ by Joel Ajide Ademola

About

🌱 Smart database seeding with run-once tracking, environment awareness, and multi-ORM support

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors