Skip to main content

Expressions

Expressions let you reference data from upstream nodes and transform it dynamically in any node's input field.

Syntax

Expressions use double curly brace syntax:

{{ expression }}

Inside the braces, you write standard JavaScript (ES6+).

Inline expressions

You can embed expressions inside string fields:

Hello, {{ $json['get_user'].name }}! Your order #{{ $json['http_trigger'].request.path_params.orderId }} is ready.

Full expression

If the entire field value is an expression, the result replaces the whole value (including non-string types):

{{ $json['get_orders'].response.body.items.length }}

This would produce a number, not a string.


The $json context

$json is the global data object available in every node. It contains the output of every previously executed node, keyed by node ID.

$json['node_id']          → output of a specific node
$json['http_trigger'] → always the incoming request data

Accessing request data

The http_trigger node always has this structure:

$json['http_trigger'].request.method          // "GET", "POST", etc.
$json['http_trigger'].request.path // "/payments/transactions"
$json['http_trigger'].request.path_params.id // path parameter {id}
$json['http_trigger'].request.query.limit // query string ?limit=10
$json['http_trigger'].request.headers['authorization']
$json['http_trigger'].request.body // parsed request body
$json['http_trigger'].request.body.user.email

A manual_trigger node (runs started from the workflow builder) uses your node id as the key and only provides:

$json['m1'].triggered_at   // RFC3339 timestamp
$json['m1'].trigger // "manual"

Accessing node outputs

For an http_request_node with ID fetch_order:

$json['fetch_order'].response.status_code     // 200
$json['fetch_order'].response.headers // response headers object
$json['fetch_order'].response.body // parsed response body
$json['fetch_order'].response.body.orderId // nested field

For a mongodb_node with ID get_customer:

$json['get_customer'].document                // found document
$json['get_customer'].document.name
$json['get_customer'].count // for find operations
$json['get_customer'].documents // for find (multiple)

For a set_node with ID prepare_data:

$json['prepare_data'].result                  // whatever you set
$json['prepare_data'].result.user

JavaScript in expressions

Full ES6+ JavaScript is supported inside expressions:

// Ternary
{{ $json['http_trigger'].request.query.limit ? parseInt($json['http_trigger'].request.query.limit) : 20 }}

// Array methods
{{ $json['get_orders'].response.body.items.filter(o => o.status === 'pending').length }}

// String methods
{{ $json['get_user'].response.body.name.toUpperCase() }}

// Math
{{ Math.round($json['calculate'].total * 100) / 100 }}

// JSON serialization
{{ JSON.stringify($json['prepare_data'].result) }}

// Date
{{ new Date().toISOString() }}

Examples

URL with path parameter

https://api.example.com/users/{{ $json['http_trigger'].request.path_params.userId }}/profile

Conditional header

{{ $json['http_trigger'].request.headers['x-forwarded-for'] || $json['http_trigger'].request.headers['x-real-ip'] }}

Response body from upstream call

{{ JSON.stringify({
user: $json['get_user'].response.body,
balance: $json['get_balance'].response.body.amount
}) }}

MongoDB filter

{ "userId": "{{ $json['http_trigger'].request.path_params.userId }}", "status": "active" }

Email subject

Order {{ $json['http_trigger'].request.path_params.orderId }} confirmed for {{ $json['get_user'].document.email }}

Loop variables (on $json)

When a node is part of a Loop Node body (connected from the loop), the engine adds iteration fields to the same $json object as everywhere else. Use bracket notation:

ExpressionMeaning
$json['$item']Current element of the array being iterated
$json['$index']Zero-based index in that array
$json['$loop']Object { item, index, count } where count is total length

Examples: {{ 'https://api.example.com/items/' + $json['$item'] }}, {{ $json['$loop'].count }}.

You can still use $json['node_id'] for upstream nodes. The loop node’s own output after completion includes results (per-iteration outputs) and count (also available on $json['<loop_node_id>'] depending on your graph).


Tips

  • Node IDs matter: when you add a node, it gets an auto-generated ID. Click the node to see or change its ID. Use descriptive IDs like get_user, fetch_order for readable expressions.
  • Debug with code_node: use a code_node to console.log($json) and inspect the full context during testing.
  • Validate early: use the workflow Validate button to catch expression errors before going live.
  • JSON vs string: when a field expects JSON (like a database filter), use {{ expression }} as the entire value rather than embedding it in a string.