# tillage

> Relational test data. Seed whole databases, not columns.

Latest version **0.3.0** (published 2026-08-11) · MIT license · 0 weekly downloads

## Install

```sh
npm install tillage
pnpm add tillage
yarn add tillage
bun add tillage
```

## Health

**Score 70/100 (B)** — status: active.

Positive: has types; esm support; no vulnerabilities; recently updated; high maintenance score; high quality score.

Warnings: low downloads; pre 1.0.

## Facts

| | |
|---|---|
| Version | 0.3.0 |
| Published | 2026-08-11 |
| First published | 2026-08-10 |
| Weekly downloads | 0 |
| License | MIT |
| TypeScript types | bundled |
| Module format | ESM + CommonJS |
| Dependencies | 1 |
| Unpacked size | 26.5 KB |
| Known vulnerabilities | 0 (+1 in 1 direct dependencies) |
| Install scripts | no |
| GitHub stars | 0 |
| Author | J. Kessler |
| Maintainers | jkessler |
| Keywords | test-data, fixtures, faker, relational, database-seeding, qa, sdet |

## Links

- npm: https://www.npmjs.com/package/tillage
- Repository: https://github.com/jk-qarepo/tillage
- Homepage: https://github.com/jk-qarepo/tillage#readme
- Issues: https://github.com/jk-qarepo/tillage/issues
- npm.io page: https://npm.io/package/tillage

## Dependencies (1)

- [@faker-js/faker](https://npm.io/package/@faker-js/faker.md) ^9.0.0

## Alternatives

- [pagerjs](https://npm.io/package/pagerjs.md) — 60 weekly downloads
- [whistle.savefor-mock](https://npm.io/package/whistle.savefor-mock.md) — 4 weekly downloads
- [@mikemajesty/zod-mock-schema](https://npm.io/package/@mikemajesty/zod-mock-schema.md) — 0 weekly downloads
- [vue-scroll-active-toc](https://npm.io/package/vue-scroll-active-toc.md) — 0 weekly downloads
- [@govuk-pay/run-amock](https://npm.io/package/@govuk-pay/run-amock.md) — 0 weekly downloads

## Recent versions

- 0.3.0 (latest) — 2026-08-11
- 0.2.0 — 2026-08-11
- 0.1.0 — 2026-08-10

## README

# Tillage

Relational test data. Seed whole databases, not columns.

## Install

```
npm i tillage
```

## Quickstart

```ts
import { seed, defineFactory, hasMany } from 'tillage';

const user = defineFactory('user', ({ faker }) => ({ name: faker.person.fullName(), email: faker.internet.email() }));
const order = defineFactory('order', ({ faker }) => ({ total: faker.number.int({ min: 10, max: 500 }), userId: 0 }));

const data = seed(42, () => { user.buildList(3, { orders: hasMany(order, 2) }); });
console.log(data.toSQL({ dialect: 'postgres' }));
```

Run it with `npx tsx examples/quickstart.ts` once the package is installed locally.

## What it does

Tillage generates referentially-consistent relational datasets. It is not a faker replacement: it wraps `@faker-js/faker` for leaf values (names, emails, numbers, and so on) and adds the piece faker does not have, relationships between records.

Define a factory per entity with `defineFactory`, then connect them:

- `belongsTo(target)`, a required foreign key to a parent
- `belongsToMaybe(target)`, an optional foreign key, useful for self-referential rows
- `hasMany(target, count)`, a parent building N children
- `oneToOne(target)`, a single linked child
- `unique(fn)`, a value that will not repeat across a factory's rows
- `inherit(key)` with `scope(values, fn)`, a field pulled from an enclosing scope, for denormalised ancestor keys (such as a multi-tenant `orgId` repeated on every table)

Wrap the whole build in `seed(masterSeed, fn)` and Tillage tracks every record it creates, in order, so the result is a coherent dataset instead of a pile of disconnected rows.

### Inherited keys for multi-tenant schemas

Real schemas often denormalise an ancestor key onto every table. Declare it once with `scope` and let descendants pull it with `inherit`, instead of passing it as an override to every build:

```ts
const customer = defineFactory('customers', ({ faker }) => ({
  orgId: inherit('orgId'),
  name: faker.person.fullName(),
}), { id: 'cuid' });

seed(42, () => {
  const org = organization.build();
  scope({ orgId: org.id }, () => {
    const c = customer.build();
    scope({ customerId: c.id }, () => pool.buildList(2)); // pool inherits orgId + customerId
  });
});
```

Declare each relationship on one side only. Use `hasMany` on the parent with a plain foreign key field on the child (such as `userId: 0`, which the parent fills in), or use `belongsTo` on the child, but not both for the same relationship. Declaring both makes `belongsTo` build a standalone parent that the `hasMany` override then replaces, so you get extra parent rows.

## Determinism and ordering

Output is deterministic for a given seed: the same `masterSeed` always produces the same data, field values included. Entities are exported in insertion order, parents before children, so `toJSON()` and `toSQL()` produce output that applies cleanly, no manual reordering of inserts required.

---
_Source: https://npm.io/package/tillage · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
