Every time I start or test a project, I hit the same wall: the app works, but the database is empty. No customers, no orders, nothing to click through. So I write a seed script. Then a slightly different one on the next project. And the one after that.
Faker solves half of it — it gives you names and emails. The annoying half is keeping the data consistent: an order has to reference a customer that actually exists, a unique column has to stay unique, an enum can only hold its allowed values. Get that wrong and your app breaks on its own fake data.
So I built fauxdata and open-sourced it. Point it at a PostgreSQL database and it fills every table with realistic data in one command:
npx fauxdata --dsn postgres://localhost/mydb
No config. It reads your existing schema and figures out the rest.
Try it in two minutes
Spin up a throwaway Postgres:
docker run -d --name shop -e POSTGRES_PASSWORD=postgres -p 5432:5432 postgres:16
Give it a schema with the tricky bits — a foreign key, a unique column, and an enum:
CREATE TYPE order_status AS ENUM ('pending', 'paid', 'shipped', 'cancelled');
CREATE TABLE customers (
id serial PRIMARY KEY,
email varchar(255) NOT NULL UNIQUE,
full_name text NOT NULL
);
CREATE TABLE orders (
id serial PRIMARY KEY,
customer_id integer NOT NULL REFERENCES customers (id),
status order_status NOT NULL,
total numeric(10, 2) NOT NULL
);
Seed it:
npx fauxdata --dsn postgres://postgres:postgres@localhost:5432/postgres --rows 20
# fauxdata: inserted 40 rows across 2 tables.
Now the data actually joins:
select c.full_name, o.total, o.status
from orders o
join customers c on c.id = o.customer_id
limit 3;
full_name | total | status
----------------+---------+----------
Andrea Gerlach | 7891.93 | shipped
Pauline Larkin | 5868.83 | paid
Leonard Rempel | 2210.50 | pending
Every customer_id points to a real customer. Every status is a valid enum. Every email is unique.
What it handles
- Referential integrity — it reads your foreign keys and inserts parents before children, so references always resolve.
- The awkward cases — self-referencing tables (an employee whose manager is another employee) and circular references between tables, handled with a two-pass insert.
-
Constraints —
NOT NULL, unique columns, enums, and composite primary keys. -
Reproducibility — pass
--seed 42and you get byte-identical data every run, which makes it reliable in CI and shareable across a team. - Insert or inspect — write straight to the database, or emit a SQL file you can review first:
npx fauxdata --dsn postgres://localhost/mydb --output seed.sql
How it works
Under the hood it's TypeScript on Node, and the pipeline is small on purpose:
-
Introspect — read tables, columns, types, keys, uniques, foreign keys, and enums straight from
information_schema/pg_catalog. - Order — build a dependency graph from the foreign keys and topologically sort it, deferring the minimum set of edges needed to break cycles and self-references.
-
Generate — pick a value per column: a config override if you gave one, then a column-name heuristic (
email,*_at,price…), then a fallback by SQL type. Uniqueness and enums are enforced as it goes. - Resolve & write — fill foreign keys from already-inserted parent rows, then insert in one transaction (or write the SQL file).
node-postgres does the talking; Faker provides the values.
Why PostgreSQL first
Because I'd rather do one database really well than five halfway. Postgres is where most of us start, so it's where fauxdata starts. MySQL and SQLite are the next targets — and since the generator works on an abstract schema model, adding them is mostly a new reader, not a rewrite.
Try it / break it
It's MIT-licensed, needs no account, and installs with npx.
If you run it against a real-world schema, I'd like to know what breaks — odd types, exotic constraints, anything. Issues and PRs welcome.

Top comments (0)