Skip to main content
Function nodes let you write TypeScript 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. Add a Function node to your decision graph
  2. Connect it between other nodes
  3. Open the node to edit its code

Basic structure

Every function node exports a typed handler that receives input and returns output:
FunctionInput is generated for you from the graph’s resolved schema at this point in the flow - the same types you see in the Interface tab. You don’t declare it; it’s already in scope.

Type checking

The editor type-checks your code in strict mode as you type. Reference a field that doesn’t exist on the input, or use a number where a string is expected, and you get a diagnostic in place - before anything runs. When you change the graph’s input schema, FunctionInput updates and any code that no longer matches is flagged. Type annotations are optional: a plain JavaScript handler is valid and runs the same. The types are there to catch mistakes, not to gate you.

Accessing previous nodes

Use input.$nodes to access outputs from any upstream node in your graph:
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

Supported libraries

Function nodes include several built-in libraries:

Using dayjs

Using big.js for precision

Making HTTP requests

Async/await

Function nodes support async operations. Use async/await for any asynchronous logic:

Debugging

Use console.log to debug your functions. Output appears in the function editor’s console and in simulation traces:

Execution limits

Function nodes have these constraints: 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.