Examples

Table of contents

  1. Team and Service Registry
    1. Directory structure
    2. Configuration
    3. Data files
    4. What is validated
  2. HCL Widget Catalog
    1. Configuration
    2. Data files
  3. CSV Product Catalog
    1. Directory structure
    2. Configuration
    3. Data files
    4. What is validated
  4. Strict Mode
    1. ENABLED mode
    2. FORCE mode
  5. Multi-Format Export
    1. JSON export
    2. YAML export
    3. JSONL / NDJSON export
  6. Export JSON products to CSV
  7. TSV catalog with CSV export
  8. TOML catalog
  9. JSONL / NDJSON catalog

Team and Service Registry

This example manages teams and their services in YAML files with cross-file validation.

Directory structure

.datacur8
configs/
  teams/
    alpha.yaml
    beta.yaml
    alpha/
      services/
        api-gateway.yaml
        user-service.yaml
    beta/
      services/
        billing.yaml

Configuration

version: "0.0.0"
strict_mode: DISABLED

types:
  - name: team
    input: yaml
    match:
      include:
        - "^configs/teams/(?P<team>[^/]+)\\.ya?ml$"
    schema:
      type: object
      required: ["id", "name"]
      properties:
        id: { type: string }
        name: { type: string }
      additionalProperties: false
    constraints:
      - id: team_id_unique
        type: unique
        key: "$.id"
      - id: team_path_matches_id
        type: path_equals_attr
        path_selector: "path.team"
        references:
          key: "$.id"
    output:
      path: "out/teams.json"
      format: json

  - name: service
    input: yaml
    match:
      include:
        - "^configs/teams/(?P<team>[^/]+)/services/(?P<service>[^/]+)\\.ya?ml$"
    schema:
      type: object
      required: ["id", "name", "teamId"]
      properties:
        id: { type: string }
        name: { type: string }
        teamId: { type: string }
      additionalProperties: false
    constraints:
      - id: service_id_unique
        type: unique
        key: "$.id"
      - id: service_team_fk
        type: foreign_key
        key: "$.teamId"
        references:
          type: team
          key: "$.id"
      - id: service_path_team_matches_teamId
        type: path_equals_attr
        path_selector: "path.team"
        references:
          key: "$.teamId"
      - id: service_file_matches_id
        type: path_equals_attr
        path_selector: "path.file"
        references:
          key: "$.id"
    output:
      path: "out/services.jsonl"
      format: jsonl

Data files

configs/teams/alpha.yaml:

id: alpha
name: Team Alpha

configs/teams/alpha/services/api-gateway.yaml:

id: api-gateway
name: API Gateway
teamId: alpha

What is validated

  • Each team has a unique id
  • The team’s folder name matches its id (e.g., alpha.yaml must have id: alpha)
  • Each service has a unique id
  • Each service’s teamId must reference an existing team
  • The service’s parent team folder matches its teamId
  • The service’s file name matches its id

HCL Widget Catalog

This example uses one HCL2 attribute file per widget, validates each item with JSON Schema, and exports an aggregate HCL catalog. It is covered by tests/example_examples_hcl_catalog_success.

Configuration

.datacur8:

version: "0.0.0"

types:
  - name: widgets
    input: hcl
    match:
      include:
        - "^data/.*\\.hcl$"
    schema:
      type: object
      required: [id, enabled, price, tags, metadata]
      properties:
        id:
          type: string
        enabled:
          type: boolean
        price:
          type: number
          minimum: 0
        tags:
          type: array
          items:
            type: string
        metadata:
          type: object
          required: [owner]
          properties:
            owner:
              type: string
      additionalProperties: false
    constraints:
      - type: unique
        key: "$.id"
    output:
      path: "out/widgets.hcl"
      format: hcl

Data files

data/widget-1.hcl:

id      = "widget-1"
enabled = true
price   = 12.5
tags    = ["hardware", "featured"]
metadata = {
  owner = "team-a"
}

data/widget-2.hcl:

id      = "widget-2"
enabled = false
price   = 8
tags    = []
metadata = {
  owner = "team-b"
}

Run datacur8 validate to check the converted objects against the schema and enforce unique widget IDs. Run datacur8 export to create out/widgets.hcl:

widgets = [{
  enabled = true
  id      = "widget-1"
  metadata = {
    owner = "team-a"
  }
  price = 12.5
  tags  = ["hardware", "featured"]
  }, {
  enabled = false
  id      = "widget-2"
  metadata = {
    owner = "team-b"
  }
  price = 8
  tags  = []
}]

Exports order items by input file path and sort object keys. The output wraps the items in a widgets array, just as JSON and YAML exports do. To use JSON output instead, set output.format: json and output.path: out/widgets.json; the input files and schema stay the same.

Top-level HCL blocks, variables, attribute references, and functions are unsupported. Use attributes with literal values or self-contained expressions; see HCL input. datacur8 tidy --write formats these files while preserving comments and attribute order.

CSV Product Catalog

This example validates a CSV product catalog with schema-guided type conversion.

Directory structure

.datacur8
data/
  products.csv
  categories.csv

Configuration

version: "0.0.0"

types:
  - name: category
    input: csv
    match:
      include:
        - "^data/categories\\.csv$"
    schema:
      type: object
      required: ["id", "name"]
      properties:
        id: { type: string }
        name: { type: string }
    constraints:
      - type: unique
        key: "$.id"
    output:
      path: "out/categories.json"
      format: json

  - name: product
    input: csv
    match:
      include:
        - "^data/products\\.csv$"
    schema:
      type: object
      required: ["sku", "name", "price", "category_id", "active"]
      properties:
        sku: { type: string }
        name: { type: string }
        price: { type: number }
        category_id: { type: string }
        active: { type: boolean }
    constraints:
      - type: unique
        key: "$.sku"
      - type: foreign_key
        key: "$.category_id"
        references:
          type: category
          key: "$.id"
    output:
      path: "out/products.json"
      format: json

Data files

data/categories.csv:

id,name
electronics,Electronics
clothing,Clothing

data/products.csv:

sku,name,price,category_id,active
LAPTOP-001,Gaming Laptop,1299.99,electronics,true
TSHIRT-001,Cotton T-Shirt,19.99,clothing,true
PHONE-001,Smartphone,799.00,electronics,false

What is validated

  • All CSV headers match schema properties
  • All required columns are present
  • price is converted to a number
  • active is converted to a boolean
  • Each product’s category_id references an existing category

Strict Mode

Strict mode prevents undeclared properties from appearing in data files.

ENABLED mode

With strict_mode: ENABLED, any object schema that doesn’t explicitly set additionalProperties is treated as additionalProperties: false.

version: "0.0.0"
strict_mode: ENABLED

types:
  - name: config
    input: json
    match:
      include:
        - "^settings/.*\\.json$"
    schema:
      type: object
      required: ["name"]
      properties:
        name: { type: string }
        tags:
          type: object
          properties:
            env: { type: string }
          # No additionalProperties set — strict mode adds false here

With ENABLED, the following file would fail because tags.region is not declared:

{
  "name": "prod",
  "tags": {
    "env": "production",
    "region": "us-east-1"
  }
}

FORCE mode

With strict_mode: FORCE, even schemas that explicitly allow additional properties have it overridden:

version: "0.0.0"
strict_mode: FORCE

types:
  - name: config
    input: json
    match:
      include:
        - "^settings/.*\\.json$"
    schema:
      type: object
      required: ["name"]
      properties:
        name: { type: string }
        metadata:
          type: object
          properties:
            version: { type: string }
          additionalProperties: true  # FORCE overrides this to false

Multi-Format Export

This example shows exporting the same data in different formats.

JSON export

output:
  path: "out/teams.json"
  format: json

Produces:

{
  "team": [
    { "id": 1, "name": "Team Alpha" },
    { "id": 2, "name": "Team Beta" }
  ]
}

YAML export

output:
  path: "out/teams.yaml"
  format: yaml

Produces:

team:
  - id: 1
    name: Team Alpha
  - id: 2
    name: Team Beta

JSONL / NDJSON export

jsonl and ndjson are equivalent output formats.

output:
  path: "out/services.jsonl"
  format: jsonl

Produces:

{"id":1,"name":"API Gateway"}
{"id":2,"name":"Billing Service"}

Each line is a minified JSON object. Items are ordered by file path for deterministic output.

Export JSON products to CSV

The working fixture is tests/example_examples_csv_export_success.

.datacur8:

version: "0.0.0"
types:
  - name: products
    input: json
    match:
      include: ['^data/.*\.json$']
    schema:
      type: object
      properties:
        name: {type: string}
        price: {type: number}
        stock: {type: integer}
      required: [name, price, stock]
      additionalProperties: false
    output:
      path: out/products.csv
      format: csv

data/1.json:

{"name":"Widget","price":12.5,"stock":3}

Run datacur8 validate, then datacur8 export. out/products.csv contains:

name,price,stock
Widget,12.5,3

Columns are alphabetically sorted from the schema rather than the first item. JSON, YAML, HCL, and CSV inputs can all produce this shape. To read it back, use input: csv with the same schema and a match pattern for the exported CSV, and select a different output path (configured outputs are excluded from discovery).

Every declared column must be present in every item. Empty strings are valid; absent/null cells, extra undeclared keys, and structured values cannot be represented. Unsupported schemas fail with exit 1, source validation errors with exit 2, and export conversion/write errors with exit 3. See CSV export for the supported subset, precision, and round-trip limits.

TSV catalog with CSV export

The working fixture is tests/example_examples_tsv_catalog_success. It covers integers, numbers, booleans, empty strings, quoted tabs/newlines and quotes, uniqueness, and extension matching.

version: "0.0.0"
types:
  - name: items
    input: csv
    csv: {delimiter: "\t"}
    match:
      include: ['^data/.*\.tsv$']
    schema:
      type: object
      properties:
        title: {type: string}
        id: {type: integer}
        active: {type: boolean}
        ratio: {type: number}
        ext: {type: string}
      required: [title, id, active, ratio, ext]
      additionalProperties: false
    constraints:
      - type: unique
        key: $.id
      - type: path_equals_attr
        path_selector: path.ext
        references: {key: $.ext}
    output:
      path: out/items.csv
      format: csv

Place CSV-style tab-delimited records in data/items.tsv. For example, this table shows the columns and scalar values (use actual tabs between fields):

title id active ratio ext
Example 1 true 1.25 tsv

Run datacur8 validate, datacur8 export, and datacur8 tidy --write. Export writes comma-delimited out/items.csv with alphabetically sorted columns. Tidy keeps the input tab-delimited and aligns cells with the sorted headers. Add csv: {delimiter: "\t"} under output for TSV export instead. The input/output delimiters default independently to comma. See CSV delimiters and TSV for quoting, permitted separators, empty data, conversion limits, and errors.

TOML catalog

This TOML 1.1.0 example is covered by tests/example_examples_toml_catalog_success. Each file is one record; nested tables stay within that record.

.datacur8:

version: "0.0.0"
types:
  - name: records
    input: toml
    match:
      include: ['^data/.*\.toml$']
    schema:
      type: object
      required: [id]
      properties:
        id: {type: string}
    output:
      format: toml
      path: out/nested/records.toml
    constraints:
      - type: unique
        key: "$.id"

data/1.toml:

id = "widget-1"
released = 2026-10-07
[owner]
team = "platform"

data/2.toml:

id = "widget-2"
released = 2026-10-08
[owner]
team = "platform"

Run datacur8 validate, then datacur8 export. The generated out/nested/records.toml contains:

records = [{id = 'widget-1', owner = {team = 'platform'}, released = '2026-10-07'}, {id = 'widget-2', owner = {team = 'platform'}, released = '2026-10-08'}]

Native dates become strings during validation/export. The aggregate is a single wrapper object, distinct from individual input records. datacur8 tidy --write retains native dates but removes comments and sorts keys. See TOML conversion rules for temporal precision, exact integer limits, null rejection, and formatting policy.

JSONL / NDJSON catalog

Working fixture: tests/example_examples_jsonl_ndjson_catalog_success.

version: "0.0.0"
types:
  - name: records
    input: ndjson
    match:
      include: ['^data/.*\.(jsonl|ndjson)$']
    schema:
      type: object
      properties:
        id: {type: string}
        count: {type: integer}
        active: {type: boolean}
      required: [id]
    constraints:
      - type: unique
        key: "$.id"
    output:
      format: jsonl
      path: out/nested/records.jsonl

data/1.ndjson contains:

{"id":"b","count":2,"active":false}
{"id":"a","count":1,"active":true}

data/2.jsonl contains:

{"id":"c","count":3,"active":true}

Run datacur8 validate, datacur8 tidy --write, then datacur8 export. Output contains three objects in file/line order (b, a, c), without an aggregate wrapper. Switching either format value between jsonl and ndjson produces the same behavior and bytes. A duplicate id on line 2 reports that file and line: 2 (and the original duplicate’s line). path.file for both records in the first file is 1; it does not become their id or line number. Nested/null properties are supported; blank lines and non-object roots fail. See the full input and conversion rules.


This site uses Just the Docs, a documentation theme for Jekyll.