DEV Community

Cover image for I Got Tired of Writing Seed Scripts for Every Project, So I Built quick-seed (an ORM-Agnostic Seeding Tool)
Miit Daga
Miit Daga

Posted on

I Got Tired of Writing Seed Scripts for Every Project, So I Built quick-seed (an ORM-Agnostic Seeding Tool)

The Problem That Wouldn't Go Away

Every developer has been there.
You finish building your database schema, spin up your dev server, and realize you need realistic, relational data that actually looks like production.

So you write a seed script.
Then another project uses Prisma instead of SQL, you write another.
Next one uses Drizzle ORM, another script.
The cycle never ends.

I got tired of this repetitive work, so I built quick-seed, a database-agnostic seeding tool that works across PostgreSQL, MySQL, SQLite, Prisma, and Drizzle.


Why Database Seeding Is So Painful

1. Writing Custom Scripts for Every Project

Each project uses a different tech stack:

  • Prisma + PostgreSQL
  • Drizzle + SQLite
  • Plain MySQL or PostgreSQL

You end up maintaining multiple scripts, each with its own quirks.

2. Managing Relationships Manually

const users = [];
for (let i = 0; i < 10; i++) {
  const user = await prisma.user.create({
    data: {
      name: faker.person.fullName(),
      email: faker.internet.email(),
    }
  });
  users.push(user);
}

for (let i = 0; i < 50; i++) {
  await prisma.post.create({
    data: {
      title: "faker.lorem.sentence(),"
      userId: faker.helpers.arrayElement(users).id
    }
  });
}
Enter fullscreen mode Exit fullscreen mode

Manually managing foreign keys is painful and error-prone.

3. Copying Faker.js Code Everywhere

Every seed file becomes a mess of:

faker.person.fullName();
faker.internet.email();
faker.lorem.paragraph();
Enter fullscreen mode Exit fullscreen mode

4. No Reusability

Your scripts are tightly coupled to the ORM and database, making reuse almost impossible.


The Solution: quick-seed

I designed quick-seed around three core principles:

  • Database agnostic - Works with PostgreSQL, MySQL, SQLite
  • ORM agnostic - Supports Prisma, Drizzle, or plain SQL
  • Declarative - Define what you want, not how to create it

How It Works

Step 1: Install and Initialize

npm install @miit-daga/quick-seed --save-dev
npx quick-seed init
Enter fullscreen mode Exit fullscreen mode

The CLI auto-detects your setup:

  • Found a prisma/schema.prisma file? Configures for Prisma.
  • Using Drizzle? Detects drizzle.config.ts.
  • Plain database? You choose your connection type.

Step 2: Define Your Schema

{
  "users": {
    "count": 10,
    "fields": {
      "name": "person.fullName",
      "email": "internet.email",
      "created_at": "date.recent"
    }
  },
  "posts": {
    "count": 50,
    "fields": {
      "title": "lorem.sentence",
      "content": "lorem.paragraphs",
      "published": "datatype.boolean",
      "user_id": { "references": "users.id" }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

quick-seed automatically resolves relationships like user_id.

Step 3: Run the Seeder

npx quick-seed seed --schema schema.json
Enter fullscreen mode Exit fullscreen mode

That’s it, your database is now populated with realistic, relational data.


Key Features

1. Automatic Relationship Resolution

Determines the correct insertion order (Users → Posts), tracks IDs, and maintains referential integrity automatically.

2. Built-in Faker.js Integration

No manual imports:

"fields": {
  "firstName": "person.firstName",
  "email": "internet.email",
  "bio": "lorem.paragraph"
}
Enter fullscreen mode Exit fullscreen mode

3. ORM Auto-Detection

Which database or ORM will you be using?
> 🟣 Prisma ORM (Auto-detected)
  🟠 Drizzle ORM (Auto-detected)
  ──────────────
  🐘 PostgreSQL
  🦭 MySQL / MariaDB
  📁 SQLite (file-based)
Enter fullscreen mode Exit fullscreen mode

4. Custom Generators

Use functions for more control:

"fields": {
  "role": (faker, db) => faker.helpers.arrayElement(['admin', 'user']),
  "displayName": (faker) => {
    const first = faker.person.firstName();
    const last = faker.person.lastName();
    return `${first}.${last}${faker.number.int({ min: 1, max: 999 })}`;
  }
}
Enter fullscreen mode Exit fullscreen mode

Real-World Example: Blog Schema

{
  "users": {
    "count": 5,
    "fields": {
      "name": "person.fullName",
      "email": "internet.email",
      "avatar": "image.avatar"
    }
  },
  "posts": {
    "count": 20,
    "fields": {
      "title": "lorem.sentence",
      "content": "lorem.paragraphs",
      "userId": { "references": "users.id" }
    }
  },
  "comments": {
    "count": 50,
    "fields": {
      "content": "lorem.paragraph",
      "userId": { "references": "users.id" },
      "postId": { "references": "posts.id" }
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Command:

npx quick-seed seed --schema blog-schema.json
Enter fullscreen mode Exit fullscreen mode

Output:

🌱 Starting seeding...
Seeding order: users → posts → comments
✅ Seeding completed in 0.12s
Enter fullscreen mode Exit fullscreen mode

Technical Deep Dive

1. Schema Parsing

Builds a dependency graph:

const dependencies = {
  users: [],
  posts: ['users'],
  comments: ['users', 'posts']
};
Enter fullscreen mode Exit fullscreen mode

2. Topological Sort

Determines correct insertion order:
users → posts → comments

3. Adapter Pattern

Each database uses an adapter:

interface IDatabaseAdapter {
  connect(connection: any): Promise<void>;
  insert(table: string, data: any[]): Promise<any[]>;
  disconnect(): Promise<void>;
}
Enter fullscreen mode Exit fullscreen mode

4. Relationship Resolver

Tracks IDs as records are inserted for valid foreign keys.


Challenges Faced

  • Different primary key strategies: MySQL doesn’t support RETURNING, so I had to use SELECT MAX(id) tricks.
  • Prisma client paths: Some projects use custom output paths, auto-detection solved this.

What I Learned

  • Developer experience matters - auto-detection saves time
  • Documentation is crucial for adoption
  • TypeScript support makes tools robust

Open Source Journey

This is my first npm package and first open source contribution.

Publishing

npm version patch
npm publish
Enter fullscreen mode Exit fullscreen mode

Sharing

  • Medium article ✅
  • LinkedIn post
  • Reddit threads (r/javascript, r/node, r/webdev)
  • Dev.to (this one!)

Future Roadmap

  • Auto-generate schemas from Prisma
  • Add NoSQL support
  • Async custom generators

Example:

"profilePicture": async (faker) => await uploadToCloudinary(faker.image.avatar())
Enter fullscreen mode Exit fullscreen mode

Try It Yourself

npm install @miit-daga/quick-seed --save-dev
npx quick-seed init
npx quick-seed seed --schema schema.json
Enter fullscreen mode Exit fullscreen mode

Links:


Conclusion

Building quick-seed taught me how to:

  • Design developer-friendly CLIs
  • Support multiple databases through adapter patterns
  • Deliver great UX through auto-detection and good defaults

If you're tired of repetitive seed scripts, give quick-seed a try.
It’s MIT licensed, open source, and actively maintained.

🌱 Happy seeding!


Miit Daga
Full-stack developer passionate about building tools that simplify developer workflows.
Connect on LinkedIn or visit miitdaga.tech.

Top comments (0)