Rule Engine JS

A powerful, flexible JavaScript rule engine for dynamic business logic evaluation. Build complex conditional logic with simple, readable syntax.
๐ Why Rule Engine JS?
Stop hardcoding business logic. Rule Engine JS lets you define complex conditional logic as data, making your applications more flexible, maintainable, and business-friendly.
// Instead of this hardcoded logic...
if (user.age >= 18 && user.role === 'admin' && user.permissions.includes('write')) {
return true;
}
// Write this declarative rule...
const rule = rules.and(
rules.gte('age', 18),
rules.eq('role', 'admin'),
rules.in('write', 'permissions')
);
const result = engine.evaluateExpr(rule, user);
โจ Key Features
- ๐ฏ Zero Dependencies - Lightweight with no external dependencies
- โก High Performance - Intelligent caching with LRU eviction
- ๐ Security First - Built-in protection against prototype pollution
- ๐งฉ Dynamic Field Comparison - Compare values across different data paths
- ๐ Type Safe - Full TypeScript support with comprehensive type definitions
- ๐ง Extensible - Easy custom operator registration
- ๐ Monitoring - Built-in performance metrics and cache statistics
๐ฆ Installation
npm install rule-engine-js
๐ฏ Quick Start
import { createRuleEngine, createRuleHelpers } from 'rule-engine-js';
// Create engine and helpers
const engine = createRuleEngine();
const rules = createRuleHelpers();
// Your data
const user = {
name: 'John Doe',
age: 28,
role: 'admin',
email: 'john@company.com',
permissions: ['read', 'write', 'delete'],
};
// Simple rule
const isAdult = rules.gte('age', 18);
console.log(engine.evaluateExpr(isAdult, user).success); // true
// Complex rule
const canAccess = rules.and(
rules.gte('age', 18),
rules.eq('role', 'admin'),
rules.validation.email('email'),
rules.in('write', 'permissions')
);
console.log(engine.evaluateExpr(canAccess, user).success); // true
๐๏ธ Core Concepts
Rules are Data
Rules are simple JSON objects that describe conditions:
// This rule...
const rule = { and: [{ gte: ['age', 18] }, { eq: ['role', 'admin'] }] };
// Is equivalent to this helper syntax...
const rule = rules.and(rules.gte('age', 18), rules.eq('role', 'admin'));
Dynamic Field Comparison
Compare values from different paths in your data:
const formData = {
password: 'secret123',
confirmPassword: 'secret123',
score: 85,
maxScore: 100,
};
const rule = rules.and(
rules.field.equals('password', 'confirmPassword'),
rules.lt('score', 'maxScore')
);
Path Resolution
Access nested data with dot notation:
const user = {
profile: {
settings: {
theme: 'dark',
notifications: true,
},
},
};
const rule = rules.eq('profile.settings.theme', 'dark');
๐ ๏ธ Common Use Cases
๐ User Access Control
```javascript
const accessRule = rules.and(
// User must be active
rules.isTrue('user.isActive'),
// Either admin OR (department match AND has permission)
rules.or(
rules.eq('user.role', 'admin'),
rules.and(
rules.field.equals('user.department', 'resource.department'),
rules.in('write', 'user.permissions')
)
)
);
const context = {
user: {
isActive: true,
role: 'editor',
department: 'engineering',
permissions: ['read', 'write'],
},
resource: { department: 'engineering' },
};
const hasAccess = engine.evaluateExpr(accessRule, context);
```
๐ฐ Dynamic Pricing & Discounts
```javascript
const discountRule = rules.or(
// VIP customers with minimum order
rules.and(rules.eq('customer.type', 'vip'), rules.gte('order.total', 100)),
// High loyalty points
rules.gte('customer.loyaltyPoints', 1000),
// Large orders
rules.gte('order.total', 200)
);
const orderData = {
customer: { type: 'vip', loyaltyPoints: 500 },
order: { total: 150 },
};
const eligible = engine.evaluateExpr(discountRule, orderData);
```
๐ Form Validation
```javascript
const validationRule = rules.and(
rules.validation.required('firstName'),
rules.validation.required('lastName'),
rules.validation.email('email'),
rules.validation.ageRange('age', 18, 120),
rules.field.equals('password', 'confirmPassword'),
rules.isTrue('agreedToTerms')
);
const formData = {
firstName: 'John',
lastName: 'Doe',
email: 'john@example.com',
age: 25,
password: 'secret123',
confirmPassword: 'secret123',
agreedToTerms: true,
};
const isValid = engine.evaluateExpr(validationRule, formData);
```
๐ฆ Loan Approval Logic
```javascript
const approvalRule = rules.and(
rules.gte('applicant.creditScore', 650),
rules.gte('applicant.income', 50000),
rules.lte('applicant.debtRatio', 0.4),
rules.gte('applicant.employmentYears', 2),
rules.between('applicant.age', [18, 70]),
rules.in('loan.purpose', ['home', 'car', 'education'])
);
const application = {
applicant: {
creditScore: 720,
income: 75000,
debtRatio: 0.25,
employmentYears: 3,
age: 32,
},
loan: {
amount: 250000,
purpose: 'home',
},
};
const approved = engine.evaluateExpr(approvalRule, application);
```
๐ Available Operators
Category |
Operators |
Description |
Comparison |
eq , neq , gt , gte , lt , lte |
Compare values with type coercion support |
Logical |
and , or , not |
Combine multiple conditions |
String |
contains , startsWith , endsWith , regex |
Text pattern matching |
Array |
in , notIn |
Check membership in arrays |
Special |
between , isNull , isNotNull |
Range and null checking |
Validation |
email , required , ageRange , oneOf |
Common validation patterns |
- LRU Caching: Expression results and path resolutions are cached
- Regex Compilation: Patterns are compiled once and reused
- Metrics Tracking: Monitor performance with built-in metrics
- Bundle Optimization: Multiple output formats (UMD, ESM, CommonJS)
// Get performance metrics
const metrics = engine.getMetrics();
console.log({
evaluations: metrics.evaluations,
cacheHits: metrics.cacheHits,
avgTime: metrics.avgTime,
});
// Get cache statistics
const cacheStats = engine.getCacheStats();
console.log(cacheStats);
๐ Security Features
- Prototype Pollution Protection: Automatically blocks dangerous paths
- Function Access Prevention: Functions are blocked by default
- Safe Path Resolution: Only accesses own properties
- Configurable Security: Adjust security settings as needed
// Secure by default
const maliciousData = { __proto__: { isAdmin: true } };
engine.resolvePath(maliciousData, '__proto__.isAdmin'); // Returns undefined
// Configure security
const engine = createRuleEngine({
allowPrototypeAccess: false, // Always false in production
strict: true, // Enable strict type checking
maxDepth: 10, // Prevent deep recursion
maxOperators: 100, // Limit complexity
});
๐จ Custom Operators
Extend the engine with your own business logic:
// Register 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; // 9 AM to 5 PM UTC
});
// Usage
const rule = rules.and(
{ isBusinessHours: ['America/New_York'] },
rules.isTrue('support.available')
);
๐ Documentation
๐ Framework Integration
Express.js Middleware
```javascript
import { createRuleEngine, createRuleHelpers } from 'rule-engine-js';
const engine = createRuleEngine();
const rules = createRuleHelpers();
function createAccessMiddleware(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
const adminRule = rules.eq('role', 'admin');
app.get('/admin/*', createAccessMiddleware(adminRule));
```
React Form Validation
```javascript
import { createRuleEngine, createRuleHelpers } from 'rule-engine-js';
const engine = createRuleEngine();
const rules = createRuleHelpers();
function useFormValidation(validationRules) {
const validateForm = (formData) => {
const results = {};
Object.entries(validationRules).forEach(([field, rule]) => {
const result = engine.evaluateExpr(rule, formData);
results[field] = {
isValid: result.success,
error: result.success ? null : result.error,
};
});
return results;
};
return { validateForm };
}
// Usage
const validationRules = {
email: rules.validation.email('email'),
password: rules.and(
rules.gte('password.length', 8),
rules.regex('password', '(?=.*[0-9])(?=.*[a-zA-Z])')
),
};
```
๐งช Testing
import { createRuleEngine, createRuleHelpers } from 'rule-engine-js';
describe('User Access Rules', () => {
const engine = createRuleEngine();
const rules = createRuleHelpers();
test('admin has full access', () => {
const user = { role: 'admin' };
const rule = rules.eq('role', 'admin');
const result = engine.evaluateExpr(rule, user);
expect(result.success).toBe(true);
});
});
๐ Benchmarks
Operation |
Speed |
Cache Hit Rate |
Simple equality |
~0.1ms |
95% |
Complex nested rules |
~2ms |
85% |
Regex operations |
~0.5ms |
90% |
Path resolution |
~0.05ms |
98% |
Benchmarks run on Node.js 18, Intel i7, with 1000 rule evaluations
๐ค Contributing
We welcome contributions! Please see our Contributing Guide for details.
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
)
- Write tests for your changes
- Ensure all tests pass (
npm test
)
- Commit your changes (
git commit -m 'Add amazing feature'
)
- Push to the branch (
git push origin feature/amazing-feature
)
- Open a Pull Request
๐ License
This project is licensed under the MIT License - see the LICENSE file for details.
๐ Acknowledgments
- Inspired by business rule engines and decision tables
- Built for modern JavaScript applications
- Designed with security and performance in mind
- JSON Schema - For data validation
- Joi - Object schema validation
- Yup - Schema builder for runtime value parsing
Made with โค๏ธ by Crafts69Guy