Code
Run a custom expression on each input item. Use this node when the built-in Transform/Switch operations aren't expressive enough — for example, computing derived values, reformatting fields, or building conditional values per item.
The Code node uses expr-lang syntax — a JavaScript-like, sandboxed expression language with built-in helpers for strings, math, dates, and per-item array operations.
Per-item by design
The Code node runs your expression once per input item and never loads the whole upstream batch into memory. For aggregations across the full batch (sum, average, count, group-by, sort, top-N, etc.), push the items into the DataStore with a DataStore Insert node and run a DataStore Query — or query your own MySQL / Postgres / Supabase connection. SQL aggregations are faster, memory-bounded by the database, and far more expressive than anything we'd build in-process.
Configuration
| Field | Description | Notes |
|---|---|---|
| Expression | The expression evaluated for each input item | Required. Has syntax highlighting and line numbers. Not templatable — write expr syntax directly, no {{ }}. |
| Write Result To Field | Where to put the result on each item | Optional. Leave blank to replace the whole item with the result. Supports nested paths like meta.score. |
Output items are always objects
If the expression returns a non-object value (number, string, array, null) and Write Result To Field is left blank, the Code node automatically wraps it as { "code_output": <value> } so downstream nodes always see object items. Set Write Result To Field explicitly to control the field name.
Variables available in expressions
| Variable | Description |
|---|---|
item | The current input item (object or scalar). |
index | Zero-based position of the current item. |
vars | Read-only access to workflow variables set via the Set Variable node. |
TIP
To mutate workflow variables, use the Set Variable node — vars is read-only inside Code expressions.
Multi-step expressions
expr-lang is expression-only — there is no return statement, no const/let variable declarations in the JS sense, and no top-level statements. Everything is a single expression whose value becomes the result. But you can still compose surprisingly powerful logic using the building blocks below.
let chains — compute intermediate values
Use let name = ...; to bind a value, then reference it later. You can chain multiple lets, and the final expression after the last ; is the result.
let price = item.amount * item.qty;
let tax = price * 0.2;
{ ...item, price: price, tax: tax, total: price + tax }This is the expr equivalent of:
js
const price = item.amount * item.qty;
const tax = price * 0.2;
return { ...item, price, tax, total: price + tax };Pipes — chain transformations
The | operator threads the left-hand value into the next call. Great for collection transforms within a single item.
item.orders | filter(.status == "paid") | map(.amount) | sum()Inside filter/map, .field is shorthand for "the current element's field."
Conditional shape — ternary instead of if/return
Instead of multiple return statements, build the result with a ternary:
let isVip = item.spend > 1000;
isVip
? { ...item, tier: "gold", discount: 0.2 }
: { ...item, tier: "standard", discount: 0 }reduce — accumulator instead of for loops
For sums, products, or "fold over a list" patterns inside a single item, use reduce(list, expr, initial) where #acc is the accumulator and # is the current element:
reduce(item.lines, #acc + #.qty * #.price, 0)Built-in helpers
A non-exhaustive list of helpers most users miss on first read. See the expr language reference for the full list.
| Category | Helpers |
|---|---|
| Strings | split, trim, lower, upper, replace, hasPrefix, hasSuffix, matches (regex) |
| Math | abs, ceil, floor, round, min, max |
| Collections | len, map, filter, find, reduce, sum, count, groupBy, sortBy, take, all, any, none |
| Dates | date(...), now(), duration("1h"), .Format("2006-01-02") |
| Type / null | type(x), ?? (nil-coalescing), ?. (optional chaining) |
Examples
Derive a field
item.price * item.quantityWith Write Result To Field = total, each item gets a new total field equal to price × quantity. Without that field set, the result becomes { "code_output": <number> } per the auto-wrap rule above.
Combine fields
{ ...item, fullName: item.firstName + " " + item.lastName }Conditional field
{ ...item, tier: item.spend > 1000 ? "gold" : "standard" }Use a workflow variable
item.value * vars.multiplierReformat a date
{ ...item, day: date(item.created_at).Format("2006-01-02") }Multi-step calculation with let
let price = item.amount * item.qty;
let tax = price * 0.2;
{ ...item, price: price, tax: tax, total: price + tax }With Write Result To Field blank, the returned object replaces the item. The final expression after the last ; is what gets emitted.
Limits
- Expressions exceeding ~1,000 AST nodes are rejected at compile time to prevent runaway evaluation.
- Each item evaluation honours the node's timeout (default 5 minutes; configurable via the standard
timeoutsetting). - Compile errors surface as node failures with line/column hints — the editor's line numbers match those positions.
WARNING
Code expressions are sandboxed — they cannot make HTTP calls, read files, or access the filesystem. For side effects, route the output through HTTP Request, Email, or Database nodes.