JSONata basics
JSONata is a JSON query and transformation language. Mappings are pure JSONata expressions: input is the source row, output is the destination payload.
The mental model
- The whole expression is a single value — usually an object.
- Bare names are field lookups on the current context (
$). .navigates into nested values;[]filters or maps arrays.$prefixes a built-in function ($lowercase,$string, …).
Patterns you’ll use constantly
Renaming a field
{ "title": name }Optional / fallback values
{ "vendor": manufacturer ? manufacturer : "Unknown" }Mapping an array of children
{
"variants": variants.{
"sku": sku,
"price": $string(price)
}
}The variants.{ … } form maps every element of variants through the
template, returning an array.
Filtering an array
images[role = "main"].urlReturns the URLs of every image whose role is "main".
Concatenating strings
{ "handle": $lowercase(name & "-" & sku) }Conditionals on the whole record
status = "enabled" ? {
"title": name,
"status": "ACTIVE"
} : {
"title": name,
"status": "ARCHIVED"
}Built-in Graftport helpers
Alongside JSONata’s own functions, every mapping has a small set of Graftport helpers in scope. They exist because each one is easy to get subtly wrong by hand, and a mapping that hand-rolls them is longer and harder to review for no benefit.
$normCountry(value)
Turns whatever the source calls a country into the two-letter code the destination expects. It understands local spellings and common misspellings (“Danmark”, “Deutschland”, “Holland”), passes real codes through, and returns nothing for a value it can’t resolve — so one unrecognisable country never sinks the rest of an address.
{ "country": $normCountry(Country) }Reach for this instead of writing your own country list. A mapping that opens with a 200-line table of country names and codes is doing by hand what one call already does.
$money(amount, currency)
Builds a complete price value, rounded to two decimals, in the shape the destination requires.
{ "priceSet": $money($net, $currency) }$giftcode(code)
Derives the gift-card code a legacy card converts to. Long legacy codes share prefixes and can’t simply be trimmed, so this derives a fixed 16-character code instead. It always returns the same code for the same input — which is what lets a customer’s printed card keep working — so never substitute your own version.
{ "code": $giftcode(GiftCardCode) }Keep reference data out of the mapping
A mapping is a transformation, not a database. A handful of inline translations (three order statuses, a couple of product types) is fine and reads well. A long literal table of values is not: nobody can review it, and it gets copied into the next mapping that needs the same data. If you’re pasting a large table into a mapping, ask engineering whether it belongs in a helper or a lookup instead.
Escape hatches
If JSONata genuinely can’t express what you need, that’s a signal to talk to engineering — most “I need a loop with state” cases turn out to be a missing template helper, not a language limit.
Testing in the editor
The mapping editor evaluates as you type: step to a real record with the Row 1 of 5 control in the Preview header and watch the output change. Use real records from a recent extract — synthetic samples lie.