Creating Mocks

Data Blocks

Data blocks hold fixture data outside your mock bodies: a product catalog, seed users, environment settings. Every mock can read them, so the data is defined once and reused everywhere. By the end of this page you will know both data block shapes and the one rule that trips everyone up.

Flat values

The simplest data block is a set of key-value pairs, available to all mocks under data.<block>.<key>:

data "settings" {
environment = "staging"
base_url = "https://api.example.com"
}

mock "GET /info" {
format = "json"
body = <<-EOF
{
"environment": "{{data.settings.environment}}",
"api": "{{data.settings.base_url}}"
}
EOF
}

Change the value in one place and every mock that references it follows. This is handy for anything repeated across bodies: hostnames, tenant ids, feature names.

Repeated sub-blocks

For collections, repeat a named sub-block. Each repetition becomes an element of an array:

data "catalog" {
product {
id = 1
name = "Widget"
price = 9.99
}
product {
id = 2
name = "Gadget"
price = 24.99
}
}

mock "GET /products" {
format = "json"
body = <<-EOF
[
{{#forEach data.catalog.product}}
{
"id": {{item.id}},
"name": "{{item.name}}",
"price": {{item.price}}
}{{^isLast}},{{/isLast}}
{{/forEach}}
]
EOF
}
$curl http://localhost:8080/products
[ { "id": 1, "name": "Widget", "price": 9.99 }, { "id": 2, "name": "Gadget", "price": 24.99 } ]
Named sub-blocks are always arrays, even when defined once. A single product block still makes data.catalog.product an array with one element, so {{data.catalog.product.name}} renders nothing. Iterate with forEach, or read one element with {{pick (itemAt data.catalog.product 0) 'name'}}.

Storing whole payloads

When each entry is a full JSON payload rather than a few fields, give the sub-block a heredoc field. A common convention is an id for lookups plus a content field holding the payload:

data "products" {
product {
id = 1
content = <<-EOF
{
"name": "Widget",
"price": 9.99,
"tags": ["hardware", "bestseller"]
}
EOF
}
product {
id = 2
content = <<-EOF
{
"name": "Gadget",
"price": 24.99,
"tags": []
}
EOF
}
}

Rendering {{item.content}} inserts the whole payload. The List and Detail From Data recipe uses this convention to serve a collection endpoint and a detail endpoint with 404 handling from a single data block.

Organizing data across files

Data blocks merge across every .hcl file Mocko loads, so fixtures usually live in their own file, away from the routes that consume them:

mocks/
  products.hcl       # mocks reading data.catalog
  users.hcl
  shared/
    data.hcl         # data "catalog", data "settings", ...

Behavior details

  • Data blocks are read-only at runtime. For state that changes between requests, use flags; a common combination is data blocks for the baseline and flags for the overrides.
  • Inside forEach and other context-changing blocks, data is out of scope just like request. Extract what you need to a variable first, as shown in Templating.
  • Dot-chaining on helper results is not valid Bigodon: (itemAt arr 0).name does not parse. That is why single-element access goes through pick.

Next

That completes the building blocks: files, the UI, templates, matching, flags, proxying, and data. The Recipes section combines them into complete, copy-pasteable scenarios.