mockingpug
Concepts

Schema DSL

Every generator type an entity's data block can use

Each entity is one JSON file, conventionally at mock/api/<entity>/schema.json:

{
  "amount": 1000,
  "data": { "fieldName": "<DSL string>" },
  "fixtures": [{ "fieldName": "an exact literal value" }],
  "bypass": false
}
  • amount: how many records to generate for this entity. Required, non-negative.
  • data: one DSL string per field. Required.
  • fixtures (optional): exact, literal records applied positionally, for rows that need to stay fixed. See Fixtures below.
  • bypass (optional, mockingpug/react only): true makes every request for this entity fall through to the real network instead of being mocked. See React guide § bypass.

Every DSL string below is parsed by core's parseFieldType(). The exact same function backs the CLI, mockingpug/next, the Vite plugin, and the manual-import path for mockingpug/react, so behavior never diverges between transports.

Scalars

DSLTypeNotes
uuidstringSeeded UUID v4, not crypto.randomUUID(). Deterministic from the seed, and dependency-free in a browser bundle.
numbernumberRandom integer in [0, 1_000_000] by default.
number.<min>-<max>numberRandom integer in [min, max], e.g. number.1-100. Negative bounds are allowed: number.-50-50.
number.incrementnumberAuto-incrementing counter, starting at 1, scoped per entity+field. Continues past the existing max when more records are appended to an entity later (not restarted at 1).
username.FSstring"First Last": a real first + last name pair.
username.NNstring"AdjectiveNoun123": an adjective + noun + number nickname.
emailstringlocal.1234@<random-domain>.
email[<domain>]stringSame, with a fixed domain: email[gmail.com]jane.492@gmail.com.
hash / hash.md5 / hash.sha256stringA hex string shaped like a digest (32/32/64 hex chars). Not a real cryptographic hash: there's nothing to verify it against, and a real one would require Node's node:crypto (unavailable in a browser bundle) or the async Web Crypto API.
loremstring6 to 24 random lorem-ipsum words.
lorem.<N>stringLorem text truncated/padded to exactly N characters.
date / date.past / date.futurestring (ISO)A timestamp within one year of a fixed reference date, before/after/around it depending on the suffix.
booleanboolean50/50 by default.
boolean.<p>booleantrue with probability p (0 to 1), e.g. boolean.0.9.
enum[a,b,c]stringUniformly random pick among the literal, comma-separated values.

Arrays

array[<inner type>].<count> generates a fixed-length array, recursing into the inner type for every element. The inner type can be any scalar above, including another array[...] (nested arrays):

{ "tags": "array[lorem.8].5" }

A crossRef inner type is not supported yet

array[data.category].3 parses without error but fails at generation time with MP-GEN-001 — the array generator doesn't know how to resolve a cross-entity reference per element (that resolution lives in a separate dependency-graph module, not reachable from inside array[...]'s item-generation loop). If you need a fixed-size set of related records today, declare separate fields instead (category_id_1, category_id_2, category_id_3), each "data.category.id". Track the roadmap for native support.

count is checked against mock.config.js's limits.maxArrayDepth by mpug doctor; see Reference → mock.config.js.

Custom dictionaries

A bare word that isn't one of the built-in types (role, department, …) is looked up in mock/data/<name>.json, a JSON array of weighted entries:

mock/data/role.json
[
  { "value": "ADMIN", "max": 5 },
  { "value": "USER", "chance": 0.9 },
  { "value": "MODER", "chance": 0.2 }
]
  • value: the literal value to emit (any JSON type, not just strings).
  • max (optional): hard cap on how many times this value can appear across the current generation run's new records. Once the cap is hit, the entry drops out of the pool for the rest of that run.
  • chance (optional): relative weight in [0, 1] among the entries that still have room under their max. Entries with no chance share the remaining probability mass evenly.

Fixtures

Every scalar and dictionary type above picks a value randomly (seed-stable, but still randomly assigned per record). That's the wrong tool when specific rows are load-bearing: a category tree where slug: "fb" is hardcoded into icon paths, cross-links, and navigation elsewhere in an app, for instance. Regenerating that as random data would scramble the names/slugs every time, breaking anything that matched on them by string.

fixtures fixes that: an array of literal record patches, applied positionally. fixtures[0] always becomes record index 0, fixtures[1] index 1, and so on, on every mpug generate run, regardless of seed:

mock/api/category/schema.json
{
  "amount": 200,
  "data": { "id": "uuid", "name": "lorem", "slug": "lorem", "icon": "lorem" },
  "fixtures": [
    { "name": "Facebook", "slug": "fb" },
    { "name": "Steam", "slug": "steam-keys" }
  ]
}

A fixture only needs to list the fields that must stay fixed. Every field it doesn't mention (id, icon above) is still schema-generated normally. Records beyond fixtures.length (here, indices 2 through 199) are entirely schema-generated, same as without fixtures at all.

Fixture values always win: even if slug's generator type changes later, or an unrelated field on the schema changes, fixtures[0].slug still comes out as "fb" on the next generate. amount must be at least fixtures.length (mpug doctor/parsing rejects it otherwise, MP-SCHEMA-014).

Slugify

slugify[<field>,<separator>] derives a field from another field on the same record: it reads that field's already-generated value, transliterates any Cyrillic characters and Latin diacritics to plain ASCII, lowercases it, and collapses everything that isn't a-z0-9 into <separator>:

{
  "amount": 1000,
  "data": {
    "id": "uuid",
    "title": "lorem.32",
    "slug": "slugify[title,-]"
  }
}

"Привет, Мир!""privet-mir". An empty separator (slugify[title,]) concatenates words instead of separating them.

The source field (title above) must be declared earlier in data: generation follows declaration order, so a slugify field can only read a value that was already produced. A missing/unknown source field fails at parse time (MP-SCHEMA-016); a source field declared after the slugify field, or a self-reference, also fails at parse time (MP-SCHEMA-017).

Unlike a fixture, a slugify field is still generated fresh from its source on every reconciliation that touches it (field added, or the source field's own type changes) — it derives a value, it doesn't fix one.

Cross-entity relations

data.<entity> and data.<entity>.<field> are the two relation forms. They behave very differently, and have their own dedicated page: Relations & Generation Order.

Typo detection

An unrecognized type (emial[gmail.com], bool, usernme.FS) fails with a SchemaError (MP-SCHEMA-001) that includes a Levenshtein-distance "did you mean" suggestion against both the built-in type list and your project's own custom dictionary names. The comparison is done on the type's base word (the part before any [...]/. suffix), so a bracket parameter never throws off the match:

unknown generator type "emial[gmail.com]"
  did you mean "email"?

Run npx mpug doctor any time to surface these before they ever reach generation.

On this page