Data - Expression
Type identifier:
data:expressionCategory: Data Operations
Description
Evaluates a mathematical or logical expression with variables. Supports arithmetic, string manipulation, date/time operations, similarity functions, and array operations. Use this node for calculations, transformations, and complex data logic.
Input Handles
Handle | Data Type | Required | Description |
|---|---|---|---|
| Varies | Varies | Dynamic handles for each variable |
Input Handle Details
Variable Handles
Input handles are dynamically generated based on the variables configuration. Each variable creates a corresponding input handle using the variable's name as the handle ID.
- Type: Defined by each variable's schema configuration
Notes:
- Only variables referenced in the expression need to be connected
- Unconnected required variables will cause errors
- Variable names must match
[a-zA-Z0-9_]pattern
Record Variables
Record and object values are keyed by field name inside the expression, in both expression languages. This holds even when the connected value comes from a node whose output uses field IDs, such as Flow - Trigger. Read fields with their names, using bracket notation when the name has spaces or punctuation:
inspection["Free Length (mm)"] > part["Max Free Length (mm)"];The Record's system properties, such as id and createdAt, keep their usual keys. Reading a field by its ID, such as inspection.freeLength, returns undefined and does not raise an error. To check the available keys, return JSON.stringify(Object.keys(inspection)) in JavaScript mode.
Output Handles
Handle | Data Type | Description |
|---|---|---|
| Varies | The result of the expression |
|
| Triggered on evaluation error |
Output Handle Details
result
- Type: Defined by the
resultschema configuration Notes:
- Result is validated against the configured
resultschema when the schema is coercible - Object results are read with field-name keys, the same keys the expression sees on Record variables
- Loosely-typed result schemas (for example:
duration, empty object schemas, and arrays of empty object schemas) keep the raw evaluated value
- Result is validated against the configured
Configuration Options
Property | Type | Required | Default | Description |
|---|---|---|---|---|
|
| Yes |
| Display label for the node |
|
| No |
| Selects the expression language (see below) |
| Array of variable definitions | Yes |
| Input variable definitions |
| Object | Yes | Number type | Result type definition |
|
| Yes |
| The expression to evaluate |
Configuration Details
variables
- Type: Array of variable definitions
Notes:
- Each variable defines an input handle
- Variable
namemust be alphanumeric with underscores - Variable
nameis used in the expression and as handle ID
expression
- Type:
string Notes:
- In
expressionmode (default), uses expr-eval syntax - In
jsmode, uses standard JavaScript evaluated inside a secure sandbox (see JavaScript Mode) - Supports extended functions for dates, strings, and vectors
- Variables are referenced by their name
- In
Both expression languages are supported when a Flow runs on v2. Existing nodes using expression mode, including saved nodes with no mode field, do not need to be rewritten in JavaScript. The other nodes and configuration in the Flow must also support v2.
In expression mode, invalid syntax, undeclared variables, or disconnected referenced variables emit error and leave result as null. Evaluation failures and results that fail schema validation fail the node rather than emitting this preparation-error output.
JavaScript Mode
When mode is set to "js", the expression is evaluated as a standard JavaScript expression inside an isolated sandbox.
Available Bindings
- All declared
variablesare exposed as global identifiers using theirname. Record and object variables are keyed by field name (see Record Variables). - The same shared helper functions described under Supported Functions (such as
dateParse,format,addDuration,cosineSimilarity,jsonParse, orlevenshtein) are available as global functions. - A
Temporalglobal provides the deterministic date/time API described below.
Temporal API
The following static APIs are available:
Type | Static methods |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The following instance APIs are available:
Type | Methods |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Standard scalar properties are available for each supported type. These include date and time fields, ISO calendar fields such as dayOfWeek, weekOfYear, and daysInMonth, timezone fields such as offset and timeZoneId, epochMilliseconds, and duration fields such as hours, sign, and blank. ZonedDateTime.epochSeconds is also available as a convenience.
from, with, arithmetic, difference, rounding, conversion, and string formatting methods accept their standard Temporal string, field object, and options object forms unless listed under Temporal Limitations.
const date = Temporal.PlainDate.from("2024-03-15");
const delivery = date.toZonedDateTime({
timeZone: "Australia/Melbourne",
plainTime: "14:30",
});
delivery.add({ days: 2 }).toInstant();Temporal Limitations
Temporal.PlainYearMonthandTemporal.PlainMonthDayare not supported result types. Their constructors and conversion methods are not available.- JavaScript constructors such as
new Temporal.Instant(...)are not available. Use staticfrommethods. epochNanosecondsandTemporal.Instant.fromEpochNanosecondsare not available. UseepochMillisecondsandfromEpochMilliseconds.toLocaleStringis not available.toJSONis not available.valueOf()returns an ISO 8601 string. Native TemporalvalueOf()throws instead.- When
Instant.until,Instant.since,ZonedDateTime.until, orZonedDateTime.sinceis called without options, the defaultlargestUnitis"hour". Pass an options object to select another unit. - Temporal objects use the ISO 8601 calendar in normal Flow data. Calendar conversion methods are exposed for compatibility, but non-ISO Temporal values may not pass the configured date result schema.
Temporal.add(...)is not a Temporal API and is not exposed. Callinstant.add(...),date.add(...), or another instance method.
Resource Limits
Each JavaScript evaluation is constrained to:
Limit | Value | Effect on the expression |
|---|---|---|
Wall-clock budget | 2.5 seconds | Long-running or infinite loops are interrupted and rejected |
Heap memory | 16 MiB | Allocations beyond this cap throw "out of memory" |
Native stack | 1 MiB | Deep or unbounded recursion throws a stack overflow |
When a limit is exceeded the node triggers the error handle with a DATA_EXPRESSION_EVALUATION_ERROR whose message describes the failure mode (such as interrupt, out of memory, or stack overflow).
Expression Mode
In the default expression mode, evaluation has a 2.5-second limit. Initial parsing, reference analysis, and time spent resolving connected inputs are not included in this limit. Exceeding the evaluation limit triggers the error handle. Invalid results and evaluation failures also trigger the error handle.
Example
{
"type": "data:expression",
"data": {
"label": "JS - Total With Tax",
"mode": "js",
"variables": [
{ "name": "price", "type": "number" },
{ "name": "taxRate", "type": "number" }
],
"result": { "name": "total", "type": "number" },
"expression": "price * (1 + taxRate)"
}
}Supported Functions
Arithmetic
Function | Description | Example |
|---|---|---|
| Basic arithmetic |
|
| Modulo |
|
| Power |
|
| Absolute value |
|
| Round up |
|
| Round down |
|
| Round |
|
| Minimum |
|
| Maximum |
|
| Square root |
|
| Natural log |
|
String Functions
Function | Description | Example |
|---|---|---|
| String length |
|
| Concatenate |
|
| Substring |
|
| Split string |
|
| Remove whitespace |
|
| Contains check |
|
| Prefix check |
|
| Replace first |
|
| Replace all |
|
| Normalise |
|
String Similarity
Function | Description | Example |
|---|---|---|
| Jaccard similarity |
|
| Edit distance |
|
| LCS |
|
levenshtein and longestCommonSubstring accept inputs of at most 2,500 characters each and throw an error for longer strings.
Date/Time Functions
Function | Description | Example |
|---|---|---|
| Current timestamp |
|
| Parse date string (with natural-language fallback) |
|
| Add duration |
|
| Format date |
|
| Round date |
|
Duration units: years, months, weeks, days, hours, minutes, seconds, milliseconds
dateParse(...) first attempts strict Temporal parsing and then falls back to natural-language parsing for phrases such as "1 week from now". When timeZone is provided, the parsed value is normalised to that timezone and returned as a zoned date-time value.
format(...) accepts an optional timeZone argument. When provided, the input is formatted using that timezone's wall-clock time, which is useful for DST-aware display such as format(appointmentUtc, "hh:mm aaa cccc do MMMM, yyyy", "Australia/Melbourne").
Recurrence Functions
These take the value of a recurrence field. Date/time arguments accept date/time values or ISO strings. Plain dates and date-times are read in the recurrence's time zone, so "2026-10-12" means midnight on 12 October where the recurrence happens. Occurrences are zoned date-times in that time zone.
Function | Description | Example |
|---|---|---|
| Occurrences from |
|
| First occurrence at or after |
|
| Last occurrence at or before |
|
| Whether |
|
| Whether any occurrence falls on that calendar date |
|
| Short summary, as shown in the editor |
|
| RFC 5545 |
|
A value that is not a recurrence, or a date/time argument that cannot be read, fails the expression with an error naming the function and argument. Every function except recurrenceDescribe fails, with the reason, for a rule without a start date that does not name its own days (see Rules without a start date), for example "A yearly recurrence needs a start date". Expansion is limited to 10,000 steps per call. A rule that would exceed that, such as a minutely rule over several years, fails with "The recurrence has too many occurrences to expand here; narrow the date range".
In JavaScript mode, occurrences are Temporal.ZonedDateTime values:
const monday = OGDate.subtract({ days: OGDate.dayOfWeek - 1 });
recurrenceOccurrences(Schedule, monday, monday.add({ days: 6 }));To use occurrences without writing an expression, see Data - Recurrence.
Array Functions
Function | Description | Example |
|---|---|---|
| Array length |
|
| Subarray |
|
| Contains |
|
| Flatten nested |
|
| Sort array |
|
Vector Functions
Function | Description | Example |
|---|---|---|
| Cosine similarity |
|
| Euclidean distance |
|
| Manhattan distance |
|
| Hamming distance |
|
JSON Functions
Function | Description | Example |
|---|---|---|
| Parse JSON |
|
| Stringify |
|
| Dynamic lookup |
|
| Encode base64 |
|
| Decode base64 |
|
Example with dynamic keys:
get(get(get(get(get(jsonParse(result), "calendars"), calendarId), "busy"), 0), "start")
Comparison
Operator | Description |
|---|---|
| Equality |
| Comparison |
| Logical |
| Conditional |
Behaviour
Execution Flow
- Validates all referenced variables are declared
- Validates all referenced variables have connected edges
- Evaluates the expression with the connected variable values
- Validates/coerces result against the configured
resultschema when supported - Returns result or triggers error
Error Handling
Scenario | Behaviour |
|---|---|
Undeclared variable referenced | Triggers |
Variable not connected | Triggers |
Required variable undefined | Error during evaluation |
Expression syntax error | Exception thrown |
Runtime evaluation error | Exception with details |
Result fails schema validation | Exception with details |
Examples
Simple Arithmetic
{
"type": "data:expression",
"data": {
"label": "Calculate Total",
"variables": [
{ "name": "price", "type": "number" },
{ "name": "quantity", "type": "number" }
],
"result": { "name": "total", "type": "number" },
"expression": "price * quantity"
}
}String Comparison
{
"type": "data:expression",
"data": {
"label": "Check Similarity",
"variables": [
{ "name": "input", "type": "string" },
{ "name": "reference", "type": "string" }
],
"result": { "name": "similarity", "type": "number" },
"expression": "levenshtein(input, reference, true)"
}
}Date Calculation
{
"type": "data:expression",
"data": {
"label": "Add 7 Days",
"variables": [{ "name": "startDate", "type": "date" }],
"result": { "name": "endDate", "type": "date" },
"expression": "addDuration(startDate, 7, 'days')"
}
}Date From String
{
"type": "data:expression",
"data": {
"label": "Parse Date String",
"variables": [{ "name": "startedAt", "type": "string" }],
"result": { "name": "parsedDate", "type": "date", "includeTime": true },
"expression": "dateParse(startedAt)"
}
}Date From String With Timezone
{
"type": "data:expression",
"data": {
"label": "Parse Date String In Timezone",
"variables": [
{ "name": "startedAt", "type": "string" },
{ "name": "timeZone", "type": "string" }
],
"result": { "name": "parsedDate", "type": "date", "includeTime": true },
"expression": "dateParse(startedAt, timeZone)"
}
}Date From Natural Language
{
"type": "data:expression",
"data": {
"label": "Natural Language Date",
"variables": [{ "name": "startedAt", "type": "string" }],
"result": { "name": "parsedDate", "type": "date", "includeTime": true },
"expression": "dateParse(startedAt)"
}
}Example input value for startedAt: "1 week from now"
Conditional Logic
{
"type": "data:expression",
"data": {
"label": "Discount Price",
"variables": [
{ "name": "price", "type": "number" },
{ "name": "isPremium", "type": "boolean" }
],
"result": { "name": "finalPrice", "type": "number" },
"expression": "isPremium ? price * 0.8 : price"
}
}