Skip to Content
DocumentationWorking with YAML SchemasExpressionsJEXL Expressions ${{ }}

JEXL Expressions ${{ }}

Loopstack uses JEXL  (JavaScript Expression Language) for evaluating JEXL expressions. Unlike template expressions, JEXL expressions preserve the data type of the result — a number stays a number, an object stays an object, and so on.

A JEXL expression must be the entire value of a YAML field (with optional surrounding whitespace). It cannot be embedded inline within text — use template expressions ({{ }}) for that.

Property Access

Simple property

${{ user.name }}

Nested property

${{ user.profile.address.city }}

Array access by index

${{ items[0] }}

Nested array access

${{ data.users[0].roles[0] }}

Comparison Operators

OperatorDescriptionExample
==Equal${{ status == "active" }}
!=Not equal${{ role != "guest" }}
<Less than${{ age < 18 }}
>Greater than${{ score > 80 }}
<=Less than or equal${{ count <= 10 }}
>=Greater than or equal${{ price >= 100 }}

Logical Operators

OperatorDescriptionExample
&&Logical AND${{ isActive && isVerified }}
||Logical OR${{ isPremium || isTrial }}

Arithmetic Operators

OperatorDescriptionExample
+Addition${{ price + tax }}
-Subtraction${{ total - discount }}
*Multiplication${{ price * quantity }}
/Division${{ total / count }}
%Modulo${{ index % 2 }}

Arithmetic can be combined with parentheses for complex calculations:

${{ (price - price * discount) * (1 + tax) }}

Ternary Expressions

Use ternary expressions for conditional values:

${{ score > 80 ? "pass" : "fail" }}

Ternary expressions can be nested:

${{ score > 90 ? "A" : score > 80 ? "B" : "C" }}

String Concatenation

${{ "Hello " + name }}

Object Literals

Create objects inline:

${{ {key: name, value: score} }}

Array Literals

Create arrays inline:

${{ [a, b, a + b] }}

Literals

JEXL expressions support string, number, and boolean literals:

  • String: ${{ "hello world" }}
  • Number: ${{ 42 }}, ${{ 3.14 }}
  • Boolean: ${{ true }}, ${{ false }}

Custom Helpers

You can define custom JEXL functions in your workflow class using the @DefineHelper() decorator. A decorated method becomes available as a function in all JEXL expressions of that workflow, using the method name.

Defining a custom helper

import { DefineHelper, Workflow } from '@loopstack/common'; @Workflow({ configFile: __dirname + '/my.workflow.yaml', }) export class MyWorkflow { @DefineHelper() sum(a: number, b: number) { return a + b; } }

Using the custom helper in YAML

Once defined, call the helper by its method name using function call syntax:

transitions: - id: calculate from: start to: end call: - tool: createChatMessage args: role: 'assistant' content: ${{ sum(10, 20) }}

Custom helpers can be combined with other JEXL features:

arguments: doubled: ${{ multiply(value, 2) }} greeting: ${{ greet("World") }} check: ${{ len("hello") > 3 }} nested: ${{ double(increment(3)) }}

Note that JEXL helpers use function call syntax (sum(10, 20)), unlike template expressions which use space-separated syntax ({{ sum 10 20 }}).

Type Preservation

JEXL expressions preserve the data type of the evaluated result:

arguments: name: ${{ user.name }} # string age: ${{ user.profile.age }} # number active: ${{ isActive }} # boolean tags: ${{ user.tags }} # array config: ${{ settings }} # object missing: ${{ user.nonexistent }} # undefined empty: ${{ nullValue }} # null

This is the key difference from template expressions ({{ }}), which always return strings.

Last updated on