Installation
Get started by installing the package via npm, yarn, or pnpm:
npm install rule-engine-js
Your First Rule
Let’s create and evaluate your first rule in just a few steps:
Import the library
import { createRuleEngine , createRuleHelpers } from 'rule-engine-js' ;
Create engine and helpers
const engine = createRuleEngine ();
const rules = createRuleHelpers ();
Define your data
const user = {
name: 'John Doe' ,
age: 25 ,
role: 'admin' ,
email: 'john@company.com' ,
};
Create and evaluate a rule
const isAdult = rules . gte ( 'age' , 18 );
const result = engine . evaluateExpr ( isAdult , user );
console . log ( result . success ); // true
Congratulations! You just created and evaluated your first rule.
Building Complex Rules
Combining Multiple Conditions
Combine multiple conditions using logical operators to create powerful validation logic:
// Check if user can access admin panel
const canAccessAdmin = rules . and (
rules . gte ( 'age' , 18 ), // Must be adult
rules . eq ( 'role' , 'admin' ), // Must be admin
rules . validation . email ( 'email' ) // Must have valid email
);
const result = engine . evaluateExpr ( canAccessAdmin , user );
console . log ( result . success ); // true
Working with Arrays
Check array membership and combine with other conditions:
const userData = {
name: 'Jane Smith' ,
permissions: [ 'read' , 'write' , 'delete' ],
tags: [ 'premium' , 'verified' ],
};
// Check if user has write permission
const hasWriteAccess = rules . in ( 'write' , 'permissions' );
// Check if user is premium
const isPremium = rules . in ( 'premium' , 'tags' );
// Combine them
const premiumWriteAccess = rules . and ( hasWriteAccess , isPremium );
console . log ( engine . evaluateExpr ( premiumWriteAccess , userData ). success ); // true
Dynamic Field Comparison
Compare values from different parts of your data structure:
Basic Comparison
With Calculated Fields
const orderData = {
order: {
total: 150 ,
subtotal: 130 ,
tax: 20 ,
},
user: {
creditLimit: 1000 ,
currentBalance: 50 ,
},
};
// Compare fields across different paths
const validation = rules . and (
rules . field . lessThan ( 'order.total' , 'user.creditLimit' ),
rules . lte ( 'order.total' , 200 )
);
Common Patterns
const accessContext = {
user: {
role: 'editor' ,
department: 'marketing' ,
permissions: [ 'read' , 'write' ],
isActive: true ,
},
resource: {
type: 'document' ,
department: 'marketing' ,
classification: 'internal' ,
},
};
const accessRule = rules . and (
// User must be active
rules . isTrue ( 'user.isActive' ),
// Role-based access
rules . or (
rules . eq ( 'user.role' , 'admin' ), // Admins can access everything
rules . and (
// Non-admins need department match and permissions
rules . field . equals ( 'user.department' , 'resource.department' ),
rules . in ( 'write' , 'user.permissions' ),
rules . in ( 'resource.classification' , [ 'public' , 'internal' ])
)
)
);
const hasAccess = engine . evaluateExpr ( accessRule , accessContext );
console . log ( 'Has access:' , hasAccess . success );
const customerData = {
customer: {
type: 'vip' ,
loyaltyPoints: 1200 ,
isFirstTime: false ,
},
order: {
subtotal: 150 ,
items: 3 ,
category: 'electronics' ,
},
};
const discountEligibility = rules . or (
// VIP customers with minimum order
rules . and (
rules . eq ( 'customer.type' , 'vip' ),
rules . gte ( 'order.subtotal' , 100 )
),
// High loyalty points
rules . gte ( 'customer.loyaltyPoints' , 1000 ),
// Large orders
rules . gte ( 'order.subtotal' , 200 ),
// First-time customer bonus
rules . and (
rules . isTrue ( 'customer.isFirstTime' ),
rules . gte ( 'order.subtotal' , 50 )
)
);
const qualifiesForDiscount = engine . evaluateExpr ( discountEligibility , customerData );
console . log ( 'Qualifies for discount:' , qualifiesForDiscount . success );
Working with Different Data Types
Strings const textData = {
title: 'JavaScript Tutorial' ,
description: 'Learn JavaScript programming' ,
filename: 'tutorial.pdf' ,
email: 'user@example.com' ,
};
const stringRules = rules . and (
rules . contains ( 'title' , 'JavaScript' ),
rules . startsWith ( 'filename' , 'tutorial' ),
rules . endsWith ( 'filename' , '.pdf' ),
rules . validation . email ( 'email' )
);
Numbers const numericData = {
price: 99.99 ,
discount: 10 ,
quantity: 5 ,
rating: 4.5 ,
};
const numericRules = rules . and (
rules . between ( 'price' , [ 10 , 200 ]),
rules . lte ( 'discount' , 20 ),
rules . gte ( 'quantity' , 1 ),
rules . gt ( 'rating' , 4.0 )
);
Arrays const arrayData = {
skills: [ 'javascript' , 'python' , 'react' ],
roles: [ 'developer' , 'team-lead' ],
scores: [ 85 , 92 , 78 ],
};
const arrayRules = rules . and (
rules . in ( 'javascript' , 'skills' ),
rules . notIn ( 'admin' , 'roles' )
);
Nested Objects const nestedData = {
user: {
profile: {
name: 'John Doe' ,
settings: {
theme: 'dark' ,
notifications: true ,
},
},
account: {
status: 'active' ,
plan: 'premium' ,
},
},
};
const nestedRules = rules . and (
rules . eq ( 'user.profile.name' , 'John Doe' ),
rules . eq ( 'user.profile.settings.theme' , 'dark' ),
rules . isTrue ( 'user.profile.settings.notifications' ),
rules . eq ( 'user.account.status' , 'active' )
);
Configuration Options
Customize the engine behavior with configuration options:
Basic Configuration
Security Settings
const engine = createRuleEngine ({
strict: true , // Enforce strict type checking
maxDepth: 10 , // Maximum rule nesting depth
enableCache: true , // Enable performance caching
maxCacheSize: 1000 , // Cache size limit
});
Best Practices
Start Simple Begin with simple rules and gradually build complexity. Test each component before combining.
Descriptive Names Use clear, descriptive variable names that indicate the purpose of each rule.
Test Thoroughly Test rules with various data scenarios including edge cases and invalid inputs.
Handle Missing Data Always check for data existence before accessing nested properties.
Always validate user input before using it in rules to prevent security vulnerabilities and unexpected behavior.
Debugging Your Rules
Understanding rule evaluation results helps you debug issues quickly:
const debugRule = rules . and (
rules . eq ( 'role' , 'admin' ),
rules . gte ( 'age' , 18 )
);
const result = engine . evaluateExpr ( debugRule , { role: 'user' , age: 25 });
if ( ! result . success ) {
console . log ( 'Rule failed!' );
console . log ( 'Failed operator:' , result . operator );
console . log ( 'Error message:' , result . error );
console . log ( 'Context:' , result . details );
}
Test individual conditions separately before combining them to identify which specific condition is failing.
Next Steps
Common Questions
Can I save rules to a database?
Yes! Rules are JSON objects that can be easily stored and retrieved: // Save to database
const rule = rules . and ( rules . eq ( 'role' , 'admin' ), rules . gte ( 'age' , 18 ));
await db . rules . save ({ name: 'admin-access' , rule: rule });
// Load from database
const savedRule = await db . rules . findByName ( 'admin-access' );
const result = engine . evaluateExpr ( savedRule . rule , userData );
Can I use rules in React components?
Absolutely! Rules work great for dynamic UI logic: function UserProfile ({ user }) {
const canEditProfile = engine . evaluateExpr (
rules . or (
rules . eq ( 'id' , user . id ),
rules . eq ( 'role' , 'admin' )
),
{ ... user , id: currentUser . id }
);
return (
< div >
< h1 > { user . name } </ h1 >
{ canEditProfile . success && < button > Edit Profile </ button > }
</ div >
);
}
How do I optimize performance?