Drizzle seed data
Seed a Drizzle + PostgreSQL app with realistic rows — drizzle-kit push, then one command. Foreign keys resolve themselves.
Drizzle apps seed data through drizzle-kit migrations plus whatever seed script the team writes — usually a seed.ts file full of db.insert(table).values() calls. It works until relations and constraints outgrow the script. This guide shows the Drizzle-native workflow, where it breaks, and the schema-driven alternative.
How Drizzle seeding normally works
Drizzle keeps the schema in TypeScript. Migrations are generated with drizzle-kit generate and applied with drizzle-kit push (dev) or drizzle-kit migrate (deploy). Seeding is a plain script:
// seed.ts
import { drizzle } from "drizzle-orm/node-postgres";
import { users } from "./schema";
import { faker } from "@faker-js/faker";
const db = drizzle(process.env.DATABASE_URL!);
for (let i = 0; i < 50; i++) {
await db.insert(users).values({
email: faker.internet.email(),
name: faker.person.fullName(),
});
}Fine for a single table. The moment orders references users, the script has to manage ids by hand — insert the user, read the id back, then insert the order. Every relation added to the schema is another layer of manual bookkeeping in the seed.
Where Drizzle seed scripts break
- Foreign keys. Children need parent ids that don't exist until the parent is inserted. The script grows
RETURNINGclauses and lookup queries, and the dependency order becomes implicit — invisible until a seed fails or, worse, silently produces orphaned rows. - Schema drift. A new NOT NULL column, an enum, a CHECK constraint — each one is a seed change. Drizzle's schema is in TypeScript and checked by the compiler; the seed script is not. The schema moves, the seed doesn't, and tests start failing for reasons nobody can explain.
- Uniform randomness.
faker.internet.email()produces plausible values, not production distributions. If your realstatuscolumn is 70%active, a flat loop gives you roughly a third of each — and query plans in tests stop matching production.
Schema-driven seeding with Weavori
Weavori reads the database schema — the same one Drizzle pushed — and generates rows from it. The Drizzle-specific workflow:
Weavori introspects tables, foreign keys, types, and constraints, resolves dependency order (parents before children), and writes via COPY. Every generated order.user_id references a real generated user — by construction, not by RETURNING clauses.
The schema is the seed
When the Drizzle schema changes and you push a new migration, the generated data changes with it. No seed file to update, because nothing in the seed duplicated the schema.
Drizzle + Weavori in CI
The loop that keeps test databases honest:
Weavori supports API keys for headless auth, standardized exit codes, and offline license validation — CI runners on restricted networks don't phone home. Full GitHub Actions / GitLab / CircleCI / Jenkins examples in the CI/CD guide.
When to keep your Drizzle seed
Honesty about boundaries: keep the seed script for deterministic unit-test fixtures — a known user the test asserts against. Keep @faker-js/faker for single values inside tests. Weavori replaces the dataset problem: filling dev, staging, and CI databases with production-shaped, FK-intact rows.
Related guides
- Postgres test data — the five ways to get test data, compared
- Postgres seed data generator — the tool comparison
- Prisma seed data — the same loop in the Prisma ecosystem
- Quickstart — first seed in minutes