Entities & relationships
An entity is a table: a named set of fields, each producing one value per row. Entities turn generators into rows you can ship.
"entities": {
"User": {
"table": "users",
"fields": {
"id": { "generator": "uuid", "primary_key": true },
"email": { "generator": "email", "unique": true }
}
}
}table is the optional physical table name (defaults to the entity name).
Field kinds
A field is exactly one of these four shapes:
| Shape | Meaning |
|---|---|
{ "generator": "name" } | run a named schema generator (dotted location.city allowed) |
{ "type": …, … } | an inline generator definition (not reused elsewhere) |
{ "computed": "expr" } | a PEL expression over sibling fields |
{ "ref": "Entity.field" } | a foreign key |
Plus optional flags on any field:
| Flag | Effect |
|---|---|
primary_key | marks the row's key (informational; also the FK target) |
unique | de-duplicate this field across rows |
nullable | the field may be null |
"fields": {
"id": { "generator": "uuid", "primary_key": true },
"first_name": { "generator": "first_name" },
"is_active": { "type": "logic", "algorithm": "boolean", "params": { "probability": 0.9 } },
"full_name": { "type": "template", "pattern": "{{self.first_name}} {{self.last_name}}" },
"user_id": { "ref": "User.id" }
}References within a row
A template or computed field can reference other fields/generators:
{{name}}— run the named generator (or, if a field of that name exists in the row, reuse its value).{{self.field}}— read a sibling field of the same row.{{obj.prop}}— a nested property of an object value.
References are memoized per row: the same reference resolves to one value, so email and full_name see the same first_name. The row is internally consistent.
Computed fields
computed evaluates a PEL expression over sibling fields — arithmetic, formatting, conditionals:
"unit_price": { "generator": "price" },
"quantity": { "generator": "qty" },
"subtotal": { "computed": "quantity * unit_price" },
"tax": { "computed": "round(subtotal * 0.18, 2)" },
"total": { "computed": "subtotal + tax" }Fields are evaluated in topological order of their dependencies, so subtotal exists before tax reads it. A dependency cycle is an error.
Foreign keys & relationships
ref makes a field point at another entity's field. Phony generates parent entities before children and picks a real parent key — joins are always valid.
"entities": {
"User": { "fields": { "id": { "generator": "uuid", "primary_key": true } } },
"Order": { "fields": {
"id": { "generator": "uuid", "primary_key": true },
"user_id": { "ref": "User.id" }
}}
}Self-references work (e.g. Category.parent_id → Category.id); make them nullable so the first rows can have no parent:
"parent_id": { "ref": "Category.id", "nullable": true }You can declare relationships explicitly for cardinality and delete semantics (used by the cloud DB-sync layer; generation only needs the ref):
"relationships": [
{ "from": "Order.user_id", "to": "User.id", "cardinality": "many-to-one", "on_delete": "cascade" }
]Scenarios
A scenario names how many rows of each entity to generate. You can have several (dev, staging, load_test):
"scenarios": {
"dev": { "User": 100, "Order": 300 },
"staging": { "User": 10000, "Order": 50000 }
}A count may be a plain integer or { "count": N }. Entities are generated in foreign-key order so parents exist before the children that reference them.
What you get back
Generating a scenario yields tables — for each entity, an array of row objects:
{
"User": [ { "id": "018f…", "first_name": "Mehmet", "email": "mehmet.yilmaz@example.com", … }, … ],
"Order": [ { "id": "018f…", "user_id": "018f…", "total": 245.90 }, … ]
}See How generation works for the row-evaluation order, seeds, and how to run a schema.