> ## 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.

# Patterns and techniques

> Essential patterns for building decision graphs: data flow control, multi-stage decisions, array processing, and validation.

This guide covers patterns you'll use regularly when building decision graphs. Start with the fundamentals (pass-through, `$nodes`, output organization) before moving to array processing and validation.

***

## Fundamentals

These patterns apply to most decision graphs.

### Data flow with passThrough

By default, **passThrough is enabled** — each node carries forward all previous data plus its own outputs. Disable it when you want to return only the node's output fields.

In the editor, the **→** icon on a node indicates passThrough is enabled.

**With passThrough (default):**

```
Input: { customer: { tier: "gold" }, order: { total: 150 } }
     ↓
Decision Table outputs: { discount: 0.15 }
     ↓
Final output: { customer: { tier: "gold" }, order: { total: 150 }, discount: 0.15 }
```

**Without passThrough:**

```
Input: { customer: { tier: "gold" }, order: { total: 150 } }
     ↓
Decision Table outputs: { discount: 0.15 }
     ↓
Final output: { discount: 0.15 }  // Only this node's output
```

Disable passThrough when:

* You want to return only the calculated results (common for final nodes)
* You need to reshape the output structure completely

### Self-reference with \$

In expression nodes, use `$` to reference values calculated earlier in the same node:

```
subtotal    = sum(map(items, #.price * #.quantity))
tax         = $.subtotal * 0.08
shipping    = $.subtotal > 100 ? 0 : 9.99
total       = $.subtotal + $.tax + $.shipping
```

Each line can reference values from previous lines using `$.fieldName`.

### Referencing previous nodes with \$nodes

Use `$nodes` to access output from any upstream node in your graph. Each node's output is available by its name.

**In expressions:**

```
$nodes.CreditScore.rating        // Output from a node named "CreditScore"
$nodes.IncomeCheck.level         // Output from a node named "IncomeCheck"
$nodes.RiskAssessment.score      // Output from a node named "RiskAssessment"
```

**In function nodes (JavaScript):**

```javascript theme={null}
export const handler = async (input) => {
  const creditRating = input.$nodes.CreditScore.rating;
  const incomeLevel = input.$nodes.IncomeCheck.level;

  return {
    eligible: creditRating === "good" && incomeLevel === "sufficient"
  };
};
```

**In decision tables:**

You can reference `$nodes` in both input conditions and output expressions:

| Customer Tier | Credit Rating                                        | → | Discount |
| ------------- | ---------------------------------------------------- | - | -------- |
| `'gold'`      | `$nodes.CreditCheck.rating == 'excellent'`           |   | `0.20`   |
| `'silver'`    | `$nodes.CreditCheck.rating in ['good', 'excellent']` |   | `0.10`   |

<Note>
  Node names are case-sensitive and must match exactly. If your node is named "Credit Score" with a space, reference it as `$nodes["Credit Score"].field`.
</Note>

### Organizing output with outputPath

Use **outputPath** to structure your output into nested objects instead of flat fields.

**Without outputPath** — all outputs merge at root level:

```json theme={null}
{
  "isEligible": true,
  "code": "eligible",
  "responsibleParty": "seller",
  "refundAmount": 89.99
}
```

**With outputPath** — organize related fields into groups:

| Node              | outputPath     | Fields                               |
| ----------------- | -------------- | ------------------------------------ |
| Eligibility check | `returnStatus` | `isEligible`, `code`, `message`      |
| Responsibility    | `resolution`   | `responsibleParty`, `refundApproved` |

Result:

```json theme={null}
{
  "returnStatus": {
    "isEligible": true,
    "code": "eligible",
    "message": "Return within policy"
  },
  "resolution": {
    "responsibleParty": "seller",
    "refundApproved": true
  }
}
```

You can also use dot notation in output field definitions like `resolution.responsibleParty` to achieve the same structure.

***

## Array and collection patterns

These patterns handle lists of items and multiple matching rules.

### Processing arrays with loop mode

When your input contains an array of items that each need evaluation, use **loop execution mode**. The node processes each array element individually and collects the results.

**Configuration:**

| Property        | Description                                                 |
| --------------- | ----------------------------------------------------------- |
| `executionMode` | Set to `loop` to iterate over an array                      |
| `inputField`    | Path to the array to process (e.g., `testResults`, `items`) |
| `outputPath`    | Where to store the results array                            |

<Note>
  Loop mode outputs an array at the root level. Without `outputPath`, you'd get unusable output like `[{ flag: "critical" }, { flag: "abnormal" }]`. Always specify `outputPath` to place results in a named field.
</Note>

**Example: Lab results interpreter**

Input data:

```json theme={null}
{
  "testResults": [
    { "testType": "glucose", "value": 260 },
    { "testType": "potassium", "value": 3.2 },
    { "testType": "hemoglobin", "value": 10.2 }
  ]
}
```

Decision table configuration:

* `executionMode`: `loop`
* `inputField`: `testResults`
* `outputPath`: `testResults`
* `passThrough`: `true`

The table evaluates each test result individually:

| Value   | Test Type      | → | Flag         | Condition         |
| ------- | -------------- | - | ------------ | ----------------- |
| `< 3.5` | `'potassium'`  |   | `'critical'` | `'Hypokalemia'`   |
| `> 200` | `'glucose'`    |   | `'abnormal'` | `'Hyperglycemia'` |
| `< 8.5` | `'hemoglobin'` |   | `'abnormal'` | `'Anemia'`        |

Output:

```json theme={null}
{
  "testResults": [
    { "testType": "glucose", "value": 260, "flag": "abnormal", "condition": "Hyperglycemia" },
    { "testType": "potassium", "value": 3.2, "flag": "critical", "condition": "Hypokalemia" },
    { "testType": "hemoglobin", "value": 10.2, "flag": null, "condition": null }
  ]
}
```

### Collecting multiple matches

When multiple rules can apply to a single input, use **collect hit policy** to return all matching rows as an array.

| Hit Policy | Behavior                           | Use when                     |
| ---------- | ---------------------------------- | ---------------------------- |
| `first`    | Returns first matching row         | Rules are mutually exclusive |
| `collect`  | Returns all matching rows as array | Multiple rules can apply     |

<Note>
  Collect mode outputs an array at the root level. Use `outputPath` to place results in a named field (e.g., `discounts.safetyFeatures`), otherwise you'll get `[{ percentage: 3 }, { percentage: 2 }]` at root.
</Note>

**Example: Safety feature discounts**

A customer's vehicle has multiple safety features. Each feature qualifies for a separate discount:

```json theme={null}
{
  "policy": {
    "safetyFeatures": ["antiTheftSystem", "dashCam", "advancedDriverAssistance"]
  }
}
```

Decision table with `hitPolicy: collect`:

| Safety Features                           | → | Discount % | Description             |
| ----------------------------------------- | - | ---------- | ----------------------- |
| `contains($, 'antiTheftSystem')`          |   | `3`        | `'Anti-theft discount'` |
| `contains($, 'dashCam')`                  |   | `2`        | `'Dash cam discount'`   |
| `contains($, 'advancedDriverAssistance')` |   | `5`        | `'ADAS discount'`       |

With `outputPath: discounts.safetyFeatures`, the output becomes:

```json theme={null}
{
  "discounts": {
    "safetyFeatures": [
      { "percentage": 3, "description": "Anti-theft discount" },
      { "percentage": 2, "description": "Dash cam discount" },
      { "percentage": 5, "description": "ADAS discount" }
    ]
  }
}
```

Then sum the discounts in an expression node:

```
sum(map(discounts.safetyFeatures, #.percentage))  // Returns 10
```

***

## Workflow patterns

These patterns control the flow of your decision graph.

### Conditional branching with switch nodes

Use **switch nodes** to route data through different paths based on conditions.

A switch node has:

* **Conditions**: Expressions that determine which path to take
* **Handles**: Output connections for each condition
* **Default**: Fallback path when no conditions match

**Example: Approval workflow**

```
                    ┌─── approved ───→ [Generate Approval]
[Evaluate] → [Switch]
                    └─── rejected ───→ [Generate Rejection]
```

Switch configuration:

```
Condition 1: evaluation.isApproved == true  → handle: "approved"
Default:                                     → handle: "rejected"
```

Each path can have different downstream nodes that produce different outputs.

**Collect mode for switches:** Set `hitPolicy: collect` on a switch node to execute **all matching branches** instead of just the first. Results from all branches are merged.

### Validation pattern

Validate input early and branch based on validity.

**Step 1: Validation table** — Create a decision table that checks for invalid conditions:

| Condition             | → | Error                       | IsValid |
| --------------------- | - | --------------------------- | ------- |
| `weight <= 0`         |   | `'Weight must be positive'` | `false` |
| `weight > 70`         |   | `'Exceeds max weight'`      | `false` |
| `length > 200`        |   | `'Exceeds max length'`      | `false` |
| *(empty - catch all)* |   |                             | `true`  |

**Step 2: Branch on validity** — Use a switch node to route:

* Valid requests → continue processing
* Invalid requests → return error response directly

```
[Input] → [Validate] → [Switch] ─── valid ───→ [Process] → [Output]
                              └─── invalid ──→ [Output]
```

This pattern prevents wasted processing on invalid data and provides clear error messages.

### Input schema validation

Add a JSON Schema to your input node to validate incoming data structure:

```json theme={null}
{
  "type": "object",
  "properties": {
    "creditScore": { "type": "number" },
    "annualIncome": { "type": "number" },
    "employmentStatus": { "type": "string" }
  },
  "required": ["creditScore", "annualIncome"]
}
```

Invalid input is rejected before any processing occurs, with a clear error indicating what's wrong.

***

## Putting it all together

Real decisions often combine multiple patterns. Here's a loan approval flow:

```
[Input with Schema]
       ↓
[Credit Score Table] ─── passThrough: true ───→ adds creditRating, creditPoints
       ↓
[Income Table] ─── passThrough: true ───→ adds incomeLevel, incomePoints
       ↓
[Calculate DTI] ─── passThrough: true ───→ adds dtiRatio
       ↓
[Rejection Reasons] ─── hitPolicy: collect, outputPath: rejectionReasons
       ↓
[Switch] ─── len(rejectionReasons) == 0 ───→ [Calculate Interest Rate]
       └─── default ───→ [Return Rejection]
```

This flow:

1. Validates input structure via schema
2. Accumulates scores from multiple evaluation tables
3. Collects all applicable rejection reasons
4. Branches based on whether any rejections exist
5. Returns either approval with rate or rejection with reasons
