Skip to main content

Overview

The RuleEngine is the core component responsible for evaluating rule expressions against data contexts. It provides high-performance rule evaluation with intelligent caching, operator management, and built-in security features.
The Rule Engine is designed to be stateless and reusable. Create a single instance and reuse it throughout your application for optimal performance.

Creating an Engine

Use the factory function to create a new engine instance:
import { createRuleEngine } from 'rule-engine-js';

// Basic engine with default settings
const engine = createRuleEngine();

// Configured engine
const engine = createRuleEngine({
  strict: true,
  maxDepth: 10,
  enableCache: true,
  maxCacheSize: 1000,
  enableDebug: false,
});

Configuration Options

strict
boolean
default:"false"
Enable strict type checking for operators. When enabled, type coercion is disabled.
maxDepth
number
default:"10"
Maximum nesting depth for rule expressions. Prevents infinite recursion.
enableCache
boolean
default:"true"
Enable LRU caching for expression results and path resolution.
maxCacheSize
number
default:"500"
Maximum number of cached expression results. Uses LRU eviction strategy.
maxOperators
number
default:"100"
Maximum number of operators allowed in a single rule expression.
enableDebug
boolean
default:"false"
Enable debug logging for rule evaluation failures.
allowPrototypeAccess
boolean
default:"false"
Allow access to prototype properties. Always keep false in production for security.

Core Methods

evaluateExpr()

Evaluate a rule expression against a data context:
const rule = { gte: ['age', 18] };
const context = { age: 25 };

const result = engine.evaluateExpr(rule, context);
console.log(result);
// { success: true }
Return Value:
{
  success: boolean;         // Whether the rule passed
  operator?: string;        // Operator that failed (if applicable)
  error?: string;          // Error message (if failed)
  details?: object;        // Additional context (if failed)
  timestamp?: number;      // Timestamp of evaluation (if failed)
}

registerOperator()

Register custom operators for business-specific logic:
engine.registerOperator('isBusinessHours', (args, context) => {
  const [timezone = 'UTC'] = args;
  const now = new Date();
  const hour = now.getUTCHours();
  return hour >= 9 && hour < 17;
});

// Usage
const rule = { isBusinessHours: ['America/New_York'] };
const result = engine.evaluateExpr(rule, {});
Parameters:
  • name (string): Operator identifier
  • handler (function): Implementation function with signature (args, context, evaluateExpr, depth) => boolean
  • options (object): Optional configuration
    • allowOverwrite (boolean): Allow replacing existing operators

Performance Methods

getMetrics()

Get real-time performance metrics:
const metrics = engine.getMetrics();

console.log(metrics);
// {
//   evaluations: 1250,      // Total evaluations
//   cacheHits: 987,         // Cache hits
//   errors: 3,              // Failed evaluations
//   totalTime: 1234.56,     // Total time (ms)
//   avgTime: 0.99           // Average time per evaluation (ms)
// }
Monitor these metrics in production to identify performance bottlenecks and optimize rule complexity.

getCacheStats()

Get cache statistics for monitoring:
const stats = engine.getCacheStats();

console.log(stats);
// {
//   expression: {
//     size: 342,
//     maxSize: 1000
//   },
//   path: {
//     size: 156,
//     maxSize: 500
//   }
// }

clearCache()

Clear all caches (expression and path resolver):
// Clear all caches
engine.clearCache();

// Useful when context data structure changes significantly
// or for testing purposes
Clearing the cache will temporarily impact performance as the cache is rebuilt. Use sparingly in production.

Utility Methods

getOperators()

Get list of all registered operators:
const operators = engine.getOperators();
console.log(operators);
// ['eq', 'neq', 'gt', 'gte', 'lt', 'lte', 'and', 'or', 'not', ...]

getConfig()

Get current engine configuration:
const config = engine.getConfig();
console.log(config);
// {
//   strict: false,
//   maxDepth: 10,
//   enableCache: true,
//   maxCacheSize: 500,
//   ...
// }

Architecture

1

Rule Validation

The engine validates rule structure, checks depth limits, and counts operators before evaluation.
2

Cache Check

Looks up the expression in the LRU cache using a composite key of expression + context.
3

Expression Evaluation

Recursively evaluates operators, resolving paths and applying operator logic.
4

Cache Storage

Successful evaluations are cached with LRU eviction when cache is full.
5

Metrics Update

Performance metrics are updated including timing, cache hits, and errors.

Caching Strategy

The Rule Engine uses a two-tier caching system:

Expression Cache

Caches complete rule evaluation results based on expression + context hash.
  • LRU Eviction: Oldest entries removed when cache is full
  • Default Size: 500 entries
  • Key Strategy: Composite of rule structure and context values

Path Resolution Cache

Caches dot-notation path lookups for nested data access.
  • LRU Eviction: Automatic cleanup of least-used paths
  • Default Size: 500 entries
  • Scope: Shared across all rule evaluations

Cache Key Generation

The engine creates intelligent cache keys based on:
  1. Expression Structure: JSON-stringified rule object
  2. Context Identity: Context ID, shape hash, or value hash
  3. Composite Key: expr:{rule}:ctx:{contextId}
For best cache performance, use consistent object shapes and provide explicit id or _id fields in your context objects.

Security Features

The engine blocks access to dangerous paths like __proto__, constructor, and prototype:
const maliciousData = { __proto__: { isAdmin: true } };
const result = engine.resolvePath(maliciousData, '__proto__.isAdmin');
// Returns: undefined (blocked)
Prevents stack overflow attacks through deeply nested rules:
const engine = createRuleEngine({ maxDepth: 5 });

// This will fail if nesting exceeds 5 levels
const deepRule = {
  and: [{ and: [{ and: [{ and: [{ and: [{ eq: ['a', 1] }] }] }] }] }]
};
Prevents resource exhaustion from overly complex rules:
const engine = createRuleEngine({ maxOperators: 50 });

// Rules with more than 50 total operators will be rejected
Functions in context data are automatically blocked:
const context = {
  user: { name: 'John' },
  dangerousFunc: () => { /* malicious code */ }
};

// Function access is prevented by PathResolver

Best Practices

Reuse Engine Instances

Create a single engine instance and reuse it across your application for optimal caching.
// Good: Singleton pattern
export const ruleEngine = createRuleEngine();

Configure Cache Size

Adjust cache size based on your rule complexity and data variability.
// High variability: larger cache
const engine = createRuleEngine({
  maxCacheSize: 2000
});

Monitor Performance

Regularly check metrics to identify slow rules and optimization opportunities.
setInterval(() => {
  const metrics = engine.getMetrics();
  if (metrics.avgTime > 5) {
    console.warn('Slow rules detected');
  }
}, 60000);

Handle Errors Gracefully

Always check the success flag and provide fallback behavior.
const result = engine.evaluateExpr(rule, context);

if (!result.success) {
  logError(result.error);
  return defaultBehavior();
}

Common Patterns

Middleware Integration

import { createRuleEngine } from 'rule-engine-js';

const engine = createRuleEngine();

function ruleMiddleware(accessRule) {
  return (req, res, next) => {
    const result = engine.evaluateExpr(accessRule, req.user);

    if (result.success) {
      next();
    } else {
      res.status(403).json({ error: 'Access denied' });
    }
  };
}

// Usage
app.get('/admin/*', ruleMiddleware({ eq: ['role', 'admin'] }));

Batch Evaluation

function evaluateBatch(rules, context) {
  const results = {};

  for (const [ruleId, rule] of Object.entries(rules)) {
    results[ruleId] = engine.evaluateExpr(rule, context);
  }

  return results;
}

// Usage
const results = evaluateBatch({
  isAdmin: { eq: ['role', 'admin'] },
  isAdult: { gte: ['age', 18] },
  hasEmail: { isNotNull: ['email'] }
}, userData);

Next Steps