7.5 Conditions, ${...} interpolation, and captured variables
Three small sub-languages thread through pipelines: the condition grammar
(for if), ${...} interpolation (in URLs), and variables (captured
with as). They're easy to confuse, so this section pins down each.
Conditions (if)
A condition is a small, checked grammar — not JavaScript. It's parsed at config time, so a malformed condition is rejected when you store the spec, not at runtime.
- Builtins (about the in-flight message):
status,ok,method,mime,isJson,isText,isBinary,name,isDirectory. header("x-thing")— read a header.- Variables:
$var.path.to.field— read a captured variable. - Literals:
'strings', numbers,true/false/null. - Operators:
== != < > <= >=,&& || !, parentheses. Numeric equality is by value.
(header("x-mode") == 'manage') && status < 300 && $order.total > 100
A condition that doesn't parse is a config-time error — you can't deploy a broken
if.
${...} interpolation (URLs)
Interpolation fills placeholders in a call URL. Resolution order:
- captured variables,
- the input body's JSON fields,
- query params.
Dot-paths are allowed (${order.customerId}). An unresolvable placeholder is a
400 — interpolation never yields a silent empty string.
GET /data/customers/${order.customerId}
Note the difference from conditions: interpolation uses ${...} and is about
building a string; conditions are an expression language about deciding
true/false.
The URL plane: ${url.…}
Alongside the data plane above, a call URL can reference the incoming
request's path and query through the reserved url root — this is how a
pipeline forwards the addressed resource (transparent wrapping). The segment
list is the peeled sub-path: the part of the request path beyond the matched
spec prefix (for a .root spec, the whole sub-path), mirroring the positional
params the query and template services expose.
| Selector | Resolves to |
|---|---|
${url.path[0]} |
first peeled segment; ${url.path[-1]} the last |
${url.path[1:]} / ${url.path[1:3]} |
a slice, /-joined (Python-style, end-exclusive) |
${url.path} |
the whole peeled path, /-joined |
${url.base[0]} / ${url.full} |
the mount-prefix segments / base+path joined |
${url.name} |
the last segment (resource name) |
${url.query.id} / ${url.query} |
one query param / the whole query string |
${url.rest} |
exact remainder after the mount, including its leading and trailing slash |
A selector that resolves to nothing is a 400, unless you mark it optional
with ? (it elides, collapsing a neighbouring /, so a/${url.path[5]?}/b →
a/b) or give a default with || (${url.query.page || '1'}). Patterns are
checked when the spec is stored, so a malformed ${url.path[} is rejected at
author time, not at run time.
${url.rest} is the transparent-proxy form: unlike segment-joined
${url.path}, it preserves the addressed path byte for byte. The canonical
passthrough wrapper can therefore be one line:
[ "GET /data/users${url.rest}" ]
Anything that isn't the url root is a data-plane dot-path as before
(${order.customerId}); a data field literally named url is shadowed by the
URL root.
Variables (as)
as: "$var" captures a step's result into a named variable (7.3) without
disturbing the in-flight message. Captured variables are then available to:
- later conditions (
$order.status == 'open'), - later interpolation (
${order.id}), and - transforms as JSONata variables (
$order; see 7.6).
A failed captured call lands as {"_errorStatus", "_errorMessage"} in the
variable, and the pipeline keeps going — so you can capture an optional lookup
and branch on whether it succeeded.
Special variables
Inside transforms you also have runtime-provided values about the current
message: $_status, $_ok, $_headers, and $_rawBody (and the body itself is
the JSONata input $). $_user / $principal describes the original caller
as {email, roles, …extra JWT claims}; it is unbound for anonymous callers.
$_url describes the triggering URL (path segments and query). These let a
pipeline mediate access without losing who called it.
Keeping them straight
| Surface | Syntax | Question it answers |
|---|---|---|
Condition (if) |
status < 300, $v.x == 'y' |
Should this step run? |
| Interpolation (URL/header) | ${order.id} |
What URL or header value do I send? |
| Transform (body) | $order.id, $sum(...) |
What's the new body? |
Next: transforms in depth.
← Previous: 7.4 Modes · Manual home · Next: 7.6 Transforms with JSONata →