> ## Documentation Index
> Fetch the complete documentation index at: https://docs.gorules.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Function nodes

> Write custom JavaScript for complex logic, API calls, and advanced data processing.

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:

```javascript theme={null}
/** @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:

```javascript theme={null}
/** @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

```javascript theme={null}
/** @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:

| Library  | Purpose                        | Example                                 |
| -------- | ------------------------------ | --------------------------------------- |
| `dayjs`  | Date and time manipulation     | `dayjs().add(7, 'days')`                |
| `big.js` | Arbitrary-precision decimals   | `new Big('0.1').plus('0.2')`            |
| `zod`    | Input validation and parsing   | `z.string().email().parse(input)`       |
| `http`   | HTTP requests (axios-like API) | `await http.get(url)`                   |
| `zen`    | Evaluate other decisions       | `await zen.evaluate(decisionId, input)` |

### Using dayjs

```javascript theme={null}
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

```javascript theme={null}
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

```javascript theme={null}
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:

```javascript theme={null}
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:

```javascript theme={null}
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:

| Limit             | Value               |
| ----------------- | ------------------- |
| Execution timeout | 5000ms (by default) |
| Memory            | Shared 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.

```javascript theme={null}
// 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 };
  }
};
```
