Skip to main content
Function nodes let you write JavaScript code for logic that expressions and decision tables can’t handle. Use them for complex algorithms, external API calls, or custom data transformations.

Creating a function node

  1. Drag a Function node onto your decision graph
  2. Connect it between other nodes
  3. Click Edit Function to open the code editor

Basic structure

Every function node exports a handler that receives input and returns output:
/** @type {Handler} */
export const handler = async (input) => {
  // Your logic here
  return {
    result: "value"
  };
};
The input parameter contains data from the previous node in your graph.

Accessing previous nodes

Use input.$nodes to access outputs from any upstream node in your graph:
/** @type {Handler} */
export const handler = async (input) => {
  // Access output from specific nodes by name
  const creditScore = input.$nodes.CreditCheck.score;
  const riskLevel = input.$nodes.RiskAssessment.level;

  return {
    approved: creditScore > 700 && riskLevel === "low"
  };
};
Use input.$nodes.NodeName.field to reference any field from an upstream node’s output. Node names are case-sensitive and must match exactly.

Example: Calculate loyalty points

/** @type {Handler} */
export const handler = async (input) => {
  const { orderTotal, customerTier } = input;

  // Base points: $1 = 1 point
  let points = Math.floor(orderTotal);

  // Tier multipliers
  const multipliers = {
    platinum: 3,
    gold: 2,
    silver: 1.5,
    bronze: 1
  };

  const multiplier = multipliers[customerTier] || 1;
  points = Math.floor(points * multiplier);

  return {
    loyaltyPoints: points,
    tier: customerTier,
    multiplier
  };
};

Supported libraries

Function nodes include several built-in libraries:
LibraryPurposeExample
dayjsDate and time manipulationdayjs().add(7, 'days')
big.jsArbitrary-precision decimalsnew Big('0.1').plus('0.2')
zodInput validation and parsingz.string().email().parse(input)
httpHTTP requests (axios-like API)await http.get(url)
zenEvaluate other decisionsawait zen.evaluate(decisionId, input)

Using dayjs

import dayjs from 'dayjs';

export const handler = async (input) => {
  const today = dayjs();
  const birthDate = dayjs(input.birthDate);
  const age = today.diff(birthDate, 'year');

  return {
    age,
    isAdult: age >= 18,
    nextBirthday: birthDate.add(age + 1, 'year').format('YYYY-MM-DD')
  };
};

Using big.js for precision

import Big from 'big.js';

export const handler = async (input) => {
  const price = new Big(input.price);
  const taxRate = new Big(input.taxRate);

  const tax = price.times(taxRate);
  const total = price.plus(tax);

  return {
    subtotal: price.toNumber(),
    tax: tax.toNumber(),
    total: total.toNumber()
  };
};

Making HTTP requests

import http from 'http';

export const handler = async (input) => {
  const response = await http.get('https://api.example.com/rates', {
    params: { currency: input.currency }
  });

  return {
    exchangeRate: response.data.rate,
    updatedAt: response.data.timestamp
  };
};

Async/await

Function nodes support async operations. Use async/await for any asynchronous logic:
export const handler = async (input) => {
  // Parallel API calls
  const [userData, orderHistory] = await Promise.all([
    fetchUser(input.userId),
    fetchOrders(input.userId)
  ]);

  return {
    user: userData,
    totalOrders: orderHistory.length,
    lifetimeValue: orderHistory.reduce((sum, o) => sum + o.total, 0)
  };
};

Debugging

Use console.log to debug your functions. Output appears in the simulator:
export const handler = async (input) => {
  console.log('Input received:', input);

  const result = calculateSomething(input);
  console.log('Calculation result:', result);

  return result;
};

Execution limits

Function nodes have these constraints:
LimitValue
Execution timeout5000ms (by default)
MemoryShared with engine
If your function exceeds the timeout, evaluation fails with an error.

Best practices

Keep functions focused — Each function should do one thing well. Use multiple function nodes for complex workflows. Handle errors gracefully — Wrap risky operations in try/catch blocks. Avoid side effects — Functions should be deterministic when possible. Same input should produce same output. Use expressions first — If the ZEN expression language can handle your logic, prefer it over function nodes for better performance.
// Handle potential errors
export const handler = async (input) => {
  try {
    const result = await riskyOperation(input);
    return { success: true, data: result };
  } catch (error) {
    console.error('Operation failed:', error.message);
    return { success: false, error: error.message };
  }
};