TypeORM seed data
TypeORM + PostgreSQL test data without writing entity factories. The schema drives generation.
TypeORM projects seed through a hand-written script: initialize a DataSource, then repository.save() rows built from entity factories or a Faker loop. It works for a single entity and grows brittle the moment relations appear. This guide shows the TypeORM-native seeding workflow, where it breaks, and the schema-driven alternative.
How TypeORM seeding normally works
TypeORM handles schema changes with migrations (typeorm migration:generate, typeorm migration:run). Seeding is not built in — teams write a script that initializes the data source and inserts rows:
// src/seed.ts
import { AppDataSource } from "./data-source";
import { User } from "./entity/User";
import { faker } from "@faker-js/faker";
await AppDataSource.initialize();
const users = AppDataSource.getRepository(User);
for (let i = 0; i < 50; i++) {
await users.save({
email: faker.internet.email(),
name: faker.person.fullName(),
});
}Fine for one table. The moment Order has a relation to User, the script has to save the user, read the generated id back, and attach it to the order — dependency order becomes manual bookkeeping.
Where TypeORM seed scripts break
- Relations.
repository.save()cascades only if you configure it and wire the objects together by hand. Without that, children are saved with a null or hard-coded foreign key, and the failure surfaces later as an orphaned row. - Schema drift. A new NOT NULL column, an enum, a CHECK constraint — each one is a seed change. The entity and migration move; the seed script does not, and tests start failing for reasons nobody can explain.
- Uniform randomness. A Faker loop produces plausible values, not production distributions. If your real
statuscolumn 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 one TypeORM migrated — and generates rows from it. The TypeORM-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 cascade configuration.
The schema is the seed
When a TypeORM migration changes the schema, the generated data changes with it. There is no seed script to update because nothing in the script duplicated the schema.
TypeORM + 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.
Which TypeORM seed command do you need?
| Goal | Command |
|---|---|
| Apply pending migrations before seeding | npx typeorm migration:run |
| Emit a migration from entity changes | npx typeorm migration:generate |
| Sync entities with no migration files | npx typeorm schema:sync |
| Run a seed script (TypeORM ships none) | ts-node src/seed.ts |
| Generate a production-shaped dataset with no seed script | npx --yes @weavori/cli generate postgres://localhost:5432/mydb |
npx typeorm migration:run
typeorm migration:run executes every pending migration in order. TypeORM has no seeding hook of its own, so this is the schema half of the workflow and your seed script is the data half. Run it first, then seed — the rows have to fit the columns the migrations just created.
npx typeorm migration:generate
typeorm migration:generate compares your entities against the live database and writes the migration that closes the gap. Entities and migrations stay in sync with each other; the seed script is checked against neither, which is exactly how it drifts.
npx typeorm schema:sync
typeorm schema:sync pushes the entity definitions straight onto the database with no migration files. It is a prototyping shortcut, not a deploy step — it can drop columns it no longer recognizes. Acceptable for a scratch database you are about to regenerate anyway.
ts-node src/seed.ts
Running the seed script is the seeding command in TypeORM; there is no typeorm db:seed. The script initializes a DataSource, saves parents, reads their generated ids back, and attaches them to children. That ordering logic is the part that breaks: it is invisible, unchecked by the compiler, and surfaces later as an orphaned row or a foreign-key violation. Point Weavori at the migrated database instead and the ordering is computed from the foreign keys.
When to keep your TypeORM seed
Honesty about boundaries: keep a seed script for deterministic unit-test fixtures — a known user the test asserts against. Keep 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
- PostgreSQL data generator — what a Postgres data generator does, and how it relates to fake and dummy data
- Postgres test data — the five ways to get test data, compared
- Postgres seed data generator — the tool comparison
- Postgres synthetic data — synthetic vs. mock vs. anonymized
- Prisma seed data and Drizzle seed data — the same loop in other stacks
- Quickstart — first seed in minutes