> ## 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();
```

## Loaders

Loaders let the engine resolve decisions by key - `engine.evaluate('pricing.json', ...)` - instead of you reading files by hand. Pass one to the `ZenEngine` constructor: either a built-in loader config (`static`, `fs`, or `zip`) or a custom async function.

### Static

The static loader serves decisions from an in-memory map. Use it when your rules ship with the application or arrive as one payload:

```javascript theme={null}
import { ZenEngine } from '@gorules/zen-engine';
import pricingRules from './pricing-rules.json' with { type: 'json' };

const engine = new ZenEngine({
  loader: {
    type: 'static',
    content: {
      'pricing.json': pricingRules
    }
  }
});

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

### File system

The `fs` loader resolves keys against a root directory - `pricing.json` maps to `./rules/pricing.json`:

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

const engine = new ZenEngine({
  loader: { type: 'fs', path: './rules' }
});

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

### Zip archive

The `zip` loader unpacks an archive in memory; every `.json` entry becomes a decision keyed by its path in the archive. This pairs naturally with [release ZIPs](/brms/deploy/releases) downloaded from the BRMS or object storage:

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

const s3 = new S3Client({ region: 'us-east-1' });
const s3Response = await s3.send(new GetObjectCommand({
  Bucket: 'my-rules-bucket',
  Key: 'release-1.0.0.zip'
}));

const engine = new ZenEngine({
  loader: {
    type: 'zip',
    bytes: Buffer.from(await s3Response.Body.transformToByteArray())
  }
});

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

### Custom loader

For any other backend - a database, a remote API, per-tenant storage - pass an async function. Combine it with `ZenDecisionContent` to cache pre-compiled decisions:

```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 });
```

## Batch evaluation

Evaluate many requests in one call. Each result reports its own success or failure, so one bad input never fails the batch:

```javascript theme={null}
const results = await engine.evaluateBatch([
  { key: 'pricing.json', context: { amount: 100 } },
  { key: 'pricing.json', context: { amount: 250 } },
  { key: 'eligibility.json', context: { age: 30 } }
]);

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

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

## Engine configuration

Override process-wide engine defaults with `overrideConfig`. Call it once at startup, before creating engines:

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

overrideConfig({
  functionTimeoutMillis: 10000,
  nodesInContext: false,
  httpAuth: false,
});
```

* `functionTimeoutMillis` — wall-clock limit for function nodes. Defaults to `5000`.
* `nodesInContext` — expose `$nodes` results to downstream nodes. Defaults to `true`; disable to reduce context size.
* `httpAuth` — allow IAM-signed requests (AWS, Azure, GCP) from the function node `http` module. Defaults to `true`.

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

**Prefer built-in loaders.** The `static`, `fs`, and `zip` loaders cover most setups without custom code; reserve callback loaders for backends the built-ins can't reach.

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


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.