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
}
});
}
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();
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
The CLI auto-detects your setup:
- Found a
prisma/schema.prismafile? 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" }
}
}
}
quick-seed automatically resolves relationships like user_id.
Step 3: Run the Seeder
npx quick-seed seed --schema schema.json
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"
}
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)
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 })}`;
}
}
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" }
}
}
}
Command:
npx quick-seed seed --schema blog-schema.json
Output:
🌱 Starting seeding...
Seeding order: users → posts → comments
✅ Seeding completed in 0.12s
Technical Deep Dive
1. Schema Parsing
Builds a dependency graph:
const dependencies = {
users: [],
posts: ['users'],
comments: ['users', 'posts']
};
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>;
}
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 useSELECT MAX(id)tricks. -
Prisma client paths:
Some projects use custom
outputpaths, 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
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())
Try It Yourself
npm install @miit-daga/quick-seed --save-dev
npx quick-seed init
npx quick-seed seed --schema schema.json
Links:
- GitHub: github.com/miit-daga/quick-seed
- npm: @miit-daga/quick-seed
- Docs: See full examples in the README
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)