Skip to main content
Version: 1.0.0

Construct and validate typed data

This tutorial runs locally, without a model or external service. It builds an invoice, compares two dates and returns a copy flagged for review.

1. Write the program​

Save the following program as invoice.tb:

invoice.tb
module examples.invoice_data

type Invoice
reference is Text length 1..40
issued_on is Date
order_date is Date
currency is Text length 3 default "CAD"
total_cents is Int min 0
flags is List of Text length 0..5 default []

function Review
takes invoice as Invoice
gives Invoice
do
if invoice.issued_on < invoice.order_date
give copy invoice { flags as ["invoice_before_order"] }
give invoice

strategy Demo
gives Invoice
do
bind draft as Invoice = record Invoice { reference as "INV-42", issued_on as date("2026-10-20"), order_date as date("2026-10-21"), total_cents as 12500 }
Review
with invoice as draft
into reviewed
give reviewed

Defaults fill currency and flags during construction. copy keeps the other fields and leaves the original invoice unchanged.

2. Run​

touba compile invoice.tb --out invoice.air.json
touba run invoice.air.json --entry strategy:examples.invoice_data/Demo --output result

The result contains currency: "CAD" and flags: ["invoice_before_order"]. The date rule belongs to your business logic: the next step can route this invoice to a human approval.

3. Check the boundaries​

  • Change the date to date("2026-02-30"): the value is refused.
  • Set total_cents to 0 - 1: the numeric contract is refused.
  • Refer to invoice.missing_field: the compiler reports the unknown field.

Handlers and models supply complete records. Source defaults apply to record construction, not to external input. Both SDKs use the same ISO dates, JSON lists and constraints.

See Typed data for the contract and Approvals with roles for the next step.