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

# Node.js Rules Engine

> Integrate GoRules into your Node.js application.

Install the ZEN Engine and evaluate your first decision in Node.js.

## Installation

<CodeGroup>
  ```bash npm theme={null}
  npm install @gorules/zen-engine
  ```

  ```bash yarn theme={null}
  yarn add @gorules/zen-engine
  ```

  ```bash pnpm theme={null}
  pnpm add @gorules/zen-engine
  ```
</CodeGroup>

## Basic usage

```javascript theme={null}
import { ZenEngine } from '@gorules/zen-engine';
import fs from 'fs';

const content = fs.readFileSync('./pricing-rules.json');

const engine = new ZenEngine();
const decision = engine.createDecision(content);

const response = await decision.evaluate({
  customer: { tier: 'gold', yearsActive: 3 },
  order: { subtotal: 150, items: 5 }
});

console.log(response.result);
// => { discount: 0.15, freeShipping: true }

engine.dispose();
```

## Loader

The loader pattern enables dynamic decision loading from any storage backend. Combined with `ZenDecisionContent` for pre-compilation, this provides optimal performance for multi-decision applications.

### File system

```javascript theme={null}
import { ZenEngine, ZenDecisionContent } from '@gorules/zen-engine';
import fs from 'fs/promises';
import path from 'path';

const cache = new Map();

const engine = new ZenEngine({
  loader: async (key) => {
    if (cache.has(key)) {
      return cache.get(key);
    }

    const buffer = await fs.readFile(path.join('./rules', key));
    const content = new ZenDecisionContent(buffer);
    cache.set(key, content);
    return content;
  }
});

const response = await engine.evaluate('pricing.json', { amount: 100 });
console.log(response.result);
```

### AWS S3

```javascript theme={null}
import { ZenEngine, ZenDecisionContent } from '@gorules/zen-engine';
import { S3Client, GetObjectCommand } from '@aws-sdk/client-s3';
import AdmZip from 'adm-zip';

const s3 = new S3Client({ region: 'us-east-1' });
const rules = new Map();

// Download and extract all decisions at startup
const command = new GetObjectCommand({
  Bucket: 'my-rules-bucket',
  Key: 'decisions.zip'
});

const s3Response = await s3.send(command);
const zipBuffer = Buffer.from(await s3Response.Body.transformToByteArray());
const zip = new AdmZip(zipBuffer);

for (const entry of zip.getEntries()) {
  if (!entry.isDirectory) {
    rules.set(entry.entryName, new ZenDecisionContent(entry.getData()));
  }
}

const engine = new ZenEngine({
  loader: async (key) => rules.get(key)
});

const response = await engine.evaluate('pricing.json', { amount: 100 });
```

### Azure Blob Storage

```javascript theme={null}
import { ZenEngine, ZenDecisionContent } from '@gorules/zen-engine';
import { BlobServiceClient } from '@azure/storage-blob';
import AdmZip from 'adm-zip';

const blobService = BlobServiceClient.fromConnectionString(process.env.AZURE_STORAGE_CONNECTION);
const container = blobService.getContainerClient('rules');
const rules = new Map();

// Download and extract all decisions at startup
const blob = container.getBlobClient('decisions.zip');
const zipBuffer = await blob.downloadToBuffer();
const zip = new AdmZip(zipBuffer);

for (const entry of zip.getEntries()) {
  if (!entry.isDirectory) {
    rules.set(entry.entryName, new ZenDecisionContent(entry.getData()));
  }
}

const engine = new ZenEngine({
  loader: async (key) => rules.get(key)
});

const response = await engine.evaluate('pricing.json', { amount: 100 });
```

### Google Cloud Storage

```javascript theme={null}
import { ZenEngine, ZenDecisionContent } from '@gorules/zen-engine';
import { Storage } from '@google-cloud/storage';
import AdmZip from 'adm-zip';

const storage = new Storage();
const bucket = storage.bucket('my-rules-bucket');
const rules = new Map();

// Download and extract all decisions at startup
const [zipBuffer] = await bucket.file('decisions.zip').download();
const zip = new AdmZip(zipBuffer);

for (const entry of zip.getEntries()) {
  if (!entry.isDirectory) {
    rules.set(entry.entryName, new ZenDecisionContent(entry.getData()));
  }
}

const engine = new ZenEngine({
  loader: async (key) => rules.get(key)
});

const response = await engine.evaluate('pricing.json', { amount: 100 });
```

## Error handling

Using try-catch:

```javascript theme={null}
try {
  const response = await decision.evaluate(input);
  console.log(response.result);
} catch (error) {
  console.error('Evaluation failed:', error.message);
}
```

Using `safeEvaluate`:

```javascript theme={null}
const response = await decision.safeEvaluate(input);

if (response.success) {
  console.log(response.data.result);
} else {
  console.error('Evaluation failed:', response.error);
}
```

## Tracing

Enable tracing to inspect decision execution:

```javascript theme={null}
const response = await decision.evaluate(input, { trace: true });

console.log(response.trace);
// Each node's input, output, and performance timing

console.log(response.performance);
// Total evaluation time
```

## Expression utilities

Evaluate ZEN expressions outside of a decision context:

```javascript theme={null}
import {
  evaluateExpression,
  evaluateUnaryExpression
} from '@gorules/zen-engine';

// Standard expressions
const sum = await evaluateExpression('a + b', { a: 5, b: 3 });
// => 8

const total = await evaluateExpression('sum(items)', { items: [1, 2, 3, 4] });
// => 10

// Unary expressions (comparison against $)
const isValid = await evaluateUnaryExpression('>= 5', { $: 10 });
// => true

const inList = await evaluateUnaryExpression('"US", "CA", "MX"', { $: 'US' });
// => true
```

Synchronous versions are also available:

```javascript theme={null}
import {
  evaluateExpressionSync,
  evaluateUnaryExpressionSync
} from '@gorules/zen-engine';

const result = evaluateExpressionSync('a * 2', { a: 10 });
// => 20
```

## Best practices

**Use `ZenDecisionContent` for caching.** Pre-compiling decisions avoids repeated parsing overhead. Cache compiled content in a `Map` keyed by decision name.

**Initialize the engine once.** Create a single `ZenEngine` instance at application startup and reuse it for all evaluations.

**Implement a loader for dynamic decisions.** The loader pattern centralizes decision loading logic and enables caching at the source.

**Call `dispose()` on shutdown.** Release engine resources when your application terminates to prevent memory leaks.
