Project

postseed

0.0
The project is in a healthy, maintained state
Postseed generates realistic seed data for PostgreSQL that never breaks a foreign key, a unique constraint or an enum, and is deterministic: the same schema, seed and version give identical output. It runs locally with a web Studio and a CLI. This gem bundles the engine and runs it with Node.js 20 or newer.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
 Dependencies
 Project Readme

Postseed

Realistic Postgres seed data. In seconds.

Paste a schema or point at your database. Postseed respects your foreign keys, enums and uniqueness, and never leaves your machine.

CI npm PyPI Gem License: MIT

npx postseed

Postseed Studio: tables, settings and the generated SQL

Why

Seed data is usually either a hand-written script that rots, or a faker call that produces rows your database rejects: orphaned foreign keys, duplicate unique values, strings longer than the column, enum labels that do not exist. Postseed reads your schema and generates data that is relationship-correct by construction, the same every time, and inserted in one transaction.

  • Relationship-correct. Tables are ordered parents first and every foreign key points at a real parent row. Composite keys, self-references and even foreign-key cycles are handled. Unique columns stay unique. Enums only get valid labels, varchar(n) stays within n, numeric(p,s) keeps its precision, and the CHECK constraints it understands are respected.
  • Deterministic. The same schema, seed and version give byte-identical output on any machine. Regenerate fixtures in CI instead of committing them; report a bug as "seed 8123".
  • Transactional. Inserts are all-or-nothing. A failure rolls everything back and names the table, row and constraint. A dry run does the real inserts, validates every constraint against your actual database, then rolls back.
  • Local-first. No account, no upload, no telemetry. Studio listens on 127.0.0.1 behind a one-time token, and the only network connection it ever makes is to your own database.
  • Three ways in. A web Studio, a scriptable CLI, and an in-browser Playground (a static site where your schema never leaves the page).

Quickstart

You need Node.js 20 or newer. For Studio and the CLI nothing else is required.

npx postseed                                  # Studio on 127.0.0.1, opens your browser

Connect to a database, choose tables, press Generate, then copy, download, or Insert into DB (try the Dry run first).

No database yet? Open the Templates tab for four starter schemas (e-commerce, multi-tenant SaaS, blog and CMS, fintech ledger), or use the CLI with a CREATE TABLE script:

npx postseed generate --schema ./schema.sql --rows 50 --seed 42 --out seed.sql
psql "$DATABASE_URL" -f seed.sql

Or go straight from your database to your database:

export DATABASE_URL="postgres://user:password@localhost:5432/mydb"
npx postseed insert --dry-run        # validate everything, then roll back
npx postseed insert --yes            # insert for real, in one transaction
Command What it does
postseed Start Studio
postseed generate Write SQL, JSON or CSV for a schema file or a live database
postseed insert Generate and insert in one transaction (--dry-run, --truncate)
postseed introspect Show the tables, keys and enums Postseed sees
postseed init Write a starter postseed.config.ts

Exit codes: 0 ok, 1 error, 2 usage, 3 a dry run found a violation. The configuration reference lists every flag.

Other languages

The same engine is published for Python and Ruby (it runs with Node.js 20+, which must be on your PATH):

pip install postseed        # also: postseed.generate("CREATE TABLE ...", rows=50, seed=42)
gem install postseed        # also: Postseed.generate("CREATE TABLE ...", rows: 50, seed: 42)

Output is identical across npm, PyPI and RubyGems.

Configuration

// postseed.config.ts   (npx postseed init)
import { defineConfig } from "postseed";

export default defineConfig({
  rows: 50,
  tableRows: { orders: 500 },
  nullRatio: 0.1,
  overrides: {
    "users.email": { template: "user{n}@example.com" },
    "orders.status": { pick: ["paid", "shipped"], weights: [3, 1] },
  },
});

Column names guide the data (email, first_name, price, *_at, is_* and many more), then the column type does, and related timestamps stay in order (updated_at is never before created_at). See Configuration.

How it stays safe

Concern What Postseed does
Your credentials Never logged; shown as postgres://user:•••@host; saved connections live in a 0600 file inside a 0700 folder, and saving is optional
Studio on your machine 127.0.0.1 only, a per-launch token, strict Host and Origin checks, no CORS, a strict content security policy
Writing to a database One transaction; dry run first; insert only the exact settings you previewed
TRUNCATE Opt-in, shows what CASCADE would also empty, and needs the database name typed in full
Remote or production-looking databases A loud warning and a name confirmation

Read the full security model, including the least-privilege role to seed with.

Documentation

Quickstart · Configuration · Determinism · Security model · FAQ · Architecture · Decisions

What is in this repository

Path What it is
packages/core @postseed/core: the engine. Browser-safe (no Node APIs): schema model, DDL parser, FK-aware planner, deterministic generators, SQL/JSON/CSV writers
packages/pg @postseed/pg: Node-only. pg_catalog introspection, the transactional inserter with dry run, local connection and history store
packages/postseed postseed: the CLI and the local Studio server (what npx postseed runs)
packages/studio The Studio web app (React, built to static files and bundled into postseed)
apps/web The website: landing page, in-browser Playground, documentation (Next.js, static export)
wrappers The PyPI and RubyGems packages

Development

pnpm install
pnpm gates          # typecheck, lint, test, build, end-to-end
pnpm bench          # performance budget
pnpm smoke          # pack, install into an empty folder, run the real CLI and Studio

Database tests need PostgreSQL: they start a throwaway cluster if initdb is installed, or use POSTSEED_TEST_DATABASE_URL. See CONTRIBUTING.

Honest limits

  • PostgreSQL only. Versions 15 to 18 are in the CI matrix.
  • English data only in this version.
  • CHECK constraints are understood when they compare columns with constants or with each other; anything more complex is generated unconstrained, listed as a note in Studio, and caught by the dry run.
  • Types without a sensible generator (geometric, range, full-text) are left to their default or NULL; override them if you need values.
  • Partitioned tables are written through the parent, so a partition (or a default partition) must exist for the generated dates.
  • Output depends on the Postseed version: pin it exactly if you commit expectations about the data.

License

MIT © Sagar Pinninti. See LICENSE.