Skip to content

MorphQL Language Reference ​

This document provides a complete reference for the Morph Query Language (MorphQL), a declarative DSL for structural data transformation.

Query Structure ​

A MorphQL query follows this basic structure:

morphql
from <source_format> to <target_format>
[transform [unsafe]
  <actions>
]

Supported formats: json, xml, object, csv, plaintext

The transform block is optional—omit it for pure format conversion:

morphql
from json to xml

Safe Mode vs Unsafe Mode ​

By default, MorphQL generates safe code that uses optional chaining (?.) to prevent crashes when accessing properties on null/undefined values. For performance-critical scenarios with validated data, you can use the unsafe keyword:

morphql
// Safe mode (default) - uses optional chaining
from object to object
transform
  set result = price / quantity

// Unsafe mode - no optional chaining, maximum performance
from object to object
transform unsafe
  set result = price / quantity

WARNING

Use unsafe only with validated/trusted input data. Unsafe mode will crash if it encounters null/undefined values.


Context Identifiers ​

MorphQL provides special identifiers to control which data context you're reading from or writing to.

Default Contexts ​

  • source - The input data (implicit when reading in set)
  • target - The output data (implicit when reading in modify or return)
morphql
// These are equivalent - 'source' is implicit in set
set name = firstName
set name = source.firstName

// These are equivalent - 'target' is implicit in modify
modify total = total * 1.1
modify total = target.total * 1.1

Explicit Context Access ​

You can explicitly specify which context to read from:

morphql
// Read from source explicitly
set result = source.price

// Read from target (useful in set to reference previously set fields)
set first = value
set second = target.first  // References the field we just set

// Mix contexts
set total = source.price + target.markup

Root Context Identifiers ​

When working inside nested sections, the source and target identifiers refer to the current scope. To access the root/parent scope, use _source and _target:

morphql
from json to object
transform
  set globalId = id  // Root source.id

  section multiple items(
    set itemId = id           // Current item's id
    set parentId = _source.id // Root source.id (parent scope)
    set globalRef = _target.globalId // Access root target field
  ) from list

Use cases for root identifiers:

  • Access parent data from within nested sections
  • Reference root-level computed fields
  • Cross-scope data relationships

TIP

_source and _target always refer to the outermost transformation scope, even when deeply nested in multiple sections or subqueries.

Iteration Index: _key ​

When inside a section multiple block, you can use the _key identifier to access the current iteration index (starting from 0).

morphql
section multiple items(
  set rank = _key + 1
  set name = productName
) from products

The _key is also available in the where clause of a section to filter by position:

morphql
section multiple firstThree(
  set val = source
) from data where _key < 3

Actions ​

Actions are the commands used inside the transform block or section blocks.

set ​

Sets a property on the target object by reading from the source data.

morphql
set fullName = firstName + " " + lastName
set total = (price * quantity) - discount
set shortSku = substring(sku, 0, 3)

modify ​

Modifies a property on the target object by reading from the target object itself. This is useful for post-processing values that have already been mapped.

morphql
set total = price * quantity
modify total = total * 1.10   # Apply a 10% markup to the already calculated total

section ​

Creates a nested object or array. Supports optional format conversion via subqueries.

Syntax:

morphql
section [multiple] <name>( [subquery] <actions> ) [from <path>] [where <condition>] [orderby <expr> [asc|desc]] [limit <count>]

Parameters:

  • multiple: Treats the source as an array and maps each item
  • subquery: Optional nested query for format conversion
  • from <expression>: Shifts the context to a specific source data source. Can be a path, the parent keyword, or a function call (e.g., spreadsheet(source)).
  • where <condition>: Filters the source data before transformation. For multiple sections, only matching items are included. For single sections, the first matching item is used. You can use _key here to filter by index.
  • orderby <expr> [asc|desc]: Sorts the source data based on an expression. Default direction is ascending (asc). Can be combined with desc for descending order.
  • limit <count>: Restricts the maximum number of items processed in the section. Can be combined with orderby to take top-N items.

Examples:

morphql
// Simple nested object
section header(
  set id = orderId
  set date = orderDate
)

// Array mapping
section multiple items(
  set sku = itemSku
  set qty = quantity
) from orderItems

// Filtering with where clause
section multiple activeUsers(
  set name = userName
  set email = userEmail
) from users where status == "active"

// Sorting and limiting
section multiple topProducts(
  set name = title
  set rating = stars
) from products orderby stars desc limit 5

// Single section with where (find first match)
section primaryContact(
  set name = contactName
  set phone = contactPhone
) from contacts where isPrimary == true

// Subquery with format conversion
section metadata(
  from xml to object
  transform
    set name = root.productName
    set price = number(root.cost)
) from xmlDataField

// Using an expression in 'from'
section multiple records(
  set val = source
) from concat(list1, list2)

clone ​

Clones the entire source object or specific fields into the target.

morphql
// Clone everything
clone()

// Clone specific fields
clone(firstName, lastName, email)

delete ​

Removes a property from the target object. Useful after clone().

morphql
clone()
delete password
delete internalId

define ​

Defines a local variable/alias for use in subsequent expressions within the same scope.

morphql
define basePrice = price * quantity
set subtotal = basePrice
set total = basePrice - discount

if (Conditional Block) ​

Executes actions conditionally.

morphql
if (status == "active") (
  set isActive = true
  set badge = "✓"
) else (
  set isActive = false
  set badge = "✗"
)

return ​

Overwrites the target object and returns immediately from the current scope. Expressions inside return read from the target object by default.

morphql
// Top level
set greeting = "Hello " + name
return greeting

// Inside section
section user(
  set id = userId
  set name = userName
  return target
)

Escaped Identifiers ​

Use backticks (`fieldname`) to use reserved keywords or special characters as identifiers:

morphql
transform
  set `multiple` = true
  set `order-id` = root.`external-id`
  set `field with spaces` = source.`another field`

Backticks can be escaped with \ when needed in a field name.


Functions ​

Functions are used within expressions to calculate or transform values. For full documentation with examples and edge cases, see the Functions Reference.

FunctionDescription
substring(str, start, [length])Extracts a part of a string. Supports negative indices.
replace(str, search, replacement)Replaces the first occurrence in a string.
split(str, [sep], [limit])Splits a string into an array.
uppercase(str) / lowercase(str)Converts case.
trim(str)Removes leading and trailing whitespace.
padstart(str, len, [char]) / padend(str, len, [char])Pads a string to a target length.
indexof(str, search)Returns index of first occurrence, or -1.
startswith(str, prefix) / endswith(str, suffix)Prefix/suffix check.
extractnumber(str)Extracts the first numeric sequence from a string.
text(val)Converts a value to a string.
number(val)Converts a value to a number.
to_base64(val) / from_base64(val)Base64 encode/decode (isomorphic).
floor(val) / ceil(val)Rounds down or up to the nearest integer.
round(val, [mode])Rounds to nearest integer ("half-up" or "half-even").
abs(val)Absolute value.
fixed(val, [decimals], [mode])Formats to fixed decimal places. Returns a string.
min(a, b, ...) / max(a, b, ...)Smallest/largest of scalar values.
if(cond, trueVal, falseVal)Inline ternary expression.
lookup(val, "k:v", ...)Dictionary lookup, returning mapped value or null.
fromUnix(timestamp)Converts Unix timestamp string to ISO 8601 string.
toUnix(isoString)Converts ISO 8601 string to Unix timestamp string.
list(v1, ...) / array(v1, ...)Creates an array literal.
aslist(val)Ensures a value is an array.
concat(arr1, arr2, ...)Concatenates arrays.
distinct(array, expr)Unique values of an expression, first-seen order.
transpose(source, key1, ...)Zips parallel arrays into row objects.
spreadsheet(array)Converts CSV-parsed rows using first row as headers.
sum(array, expr)Sums a numeric expression per item.
avg(array, expr)Average of a numeric expression per item.
minof(array, expr) / maxof(array, expr)Min/max of a numeric expression per item.
every(array, expr) / some(array, expr)Boolean aggregates over an array.
groupby(array, expr)Groups array into [{ key, items }] objects.
extract(obj, spec...)Extracts and renames fields from an object.
unpack(str, spec...) / pack(obj, spec...)Fixed-width string read/write.
xmlnode(val, [attrKey, attrVal...])Wraps a value for XML output with attributes.

Operators ​

Arithmetic ​

OperatorDescription
+Addition / String concatenation
-Subtraction / Unary minus
*Multiplication
/Division
%Modulo / Remainder

Comparison ​

OperatorDescription
==Loose equality
===Strict equality
!=Loose inequality
!==Strict inequality
<Less than
>Greater than
<=Less than or equal
>=Greater than or equal

Logical ​

OperatorDescription
&&Logical AND
||Logical OR
!Logical NOT

Grouping ​

Use parentheses ( ) to control operator precedence:

morphql
set total = (price * quantity) - discount
set isValid = (status == "active") && (count > 0)

Complete Examples ​

E-commerce Order Transformation ​

morphql
from json to json
transform
  set orderId = id
  set customerName = customer.firstName + " " + customer.lastName

  section multiple lineItems(
    set sku = substring(productCode, 0, 6)
    set unitPrice = number(price)
    set total = number(price) * quantity
  ) from items

  section summary(
    set itemCount = items.length
    set status = if(isPaid, "Paid", "Pending")
  )

XML to JSON Conversion with Mapping ​

morphql
from xml to json
transform
  define product = root.catalog.product
  set name = product.name
  set price = number(product.price)
  set inStock = product.stock > 0

  section multiple variants(
    set color = variant.color
    set size = uppercase(variant.size)
  ) from product.variants

Spreadsheet Transformation (CSV) ​

Useful when working with CSV data where the first row contains values that should be mapped as keys for subsequent rows.

morphql
from csv to json
transform
  // spreadsheet() converts an array of objects (like CSV rows)
  // into a structured array using the first row values as keys.
  set data = spreadsheet(rows)

Format Conversion Only ​

morphql
from json to xml
morphql
from xml to object

Released under the MIT License.