
JsonRules Engine
A rule engine for JavaScript & TypeScript
Define business logic with JSON rules

Overview
JsonRules is a rule engine that allows you to define business logic as JSON and evaluate it against data. It provides a simple way to externalize decision-making logic from your application code.
Use Cases
- E-commerce: Pricing rules, discount eligibility, shipping calculations
- User Management: Access control, feature flags, user segmentation
- Content: Filtering, moderation, recommendations
- Workflows: Approval processes, notifications
- Configuration: A/B testing, feature rollouts
Features
- Type-safe with full TypeScript support
- 40+ built-in operators for common operations
- Support for complex nested conditions
- Template variables for dynamic rules
- Validation for rule structure and syntax
- Works in Node.js and browsers
Installation
npm install @ivandt/json-rules
Dependencies
This library has one dependency:
validator - Used for advanced validation operators (email, URL, phone numbers, etc.)
Quick Start
import { JsonRules, Rule } from "@ivandt/json-rules";
const rule: Rule = {
conditions: {
all: [
{ field: "age", operator: "is greater than or equal", value: 18 },
{ field: "country", operator: "is equal", value: "US" }
]
}
};
const user = { age: 25, country: "US" };
const result = await JsonRules.evaluate(rule, user);
console.log(result);
Operators
JsonRules provides operators for common data operations:
Equality & Comparison
is equal | Equal to | string | number | boolean | Date | null | { field: "status", operator: "is equal", value: "active" } |
is not equal | Not equal to | string | number | boolean | Date | null | { field: "status", operator: "is not equal", value: "banned" } |
is greater than | Greater than comparison | string | number | Date | { field: "age", operator: "is greater than", value: 18 } |
is less than | Less than comparison | string | number | Date | { field: "price", operator: "is less than", value: 100 } |
is greater than or equal | Greater than or equal | string | number | Date | { field: "score", operator: "is greater than or equal", value: 80 } |
is less than or equal | Less than or equal | string | number | Date | { field: "items", operator: "is less than or equal", value: 10 } |
Range & Between
is between numbers | Number within range (inclusive) | [number, number] | { field: "age", operator: "is between numbers", value: [18, 65] } |
is not between numbers | Number outside range | [number, number] | { field: "temperature", operator: "is not between numbers", value: [32, 100] } |
is between dates | Date within range (inclusive) | [Date, Date] | { field: "eventDate", operator: "is between dates", value: [startDate, endDate] } |
is not between dates | Date outside range | [Date, Date] | { field: "blackoutDate", operator: "is not between dates", value: [holiday1, holiday2] } |
Collection & Array
in | Value exists in array | (string | number | boolean | object | null)[] | { field: "country", operator: "in", value: ["US", "CA", "UK"] } |
not in | Value not in array | (string | number | boolean | object | null)[] | { field: "status", operator: "not in", value: ["banned", "suspended"] } |
array contains | Array field contains value | string | number | boolean | object | null | { field: "skills", operator: "array contains", value: "javascript" } |
array no contains | Array field doesn't contain value | string | number | boolean | object | null | { field: "permissions", operator: "array no contains", value: "admin" } |
String Operations
contains | String contains substring | string | { field: "email", operator: "contains", value: "@company.com" } |
not contains | String doesn't contain substring | string | { field: "message", operator: "not contains", value: "spam" } |
contains any | String contains any substring | string[] | { field: "title", operator: "contains any", value: ["urgent", "critical"] } |
not contains any | String contains none of substrings | string[] | { field: "content", operator: "not contains any", value: ["spam", "scam"] } |
starts with | String starts with prefix | string | { field: "productCode", operator: "starts with", value: "PRD-" } |
ends with | String ends with suffix | string | { field: "filename", operator: "ends with", value: ".pdf" } |
Pattern Matching
matches | Matches regex pattern | { regex: string, flags?: string } | { field: "email", operator: "matches", value: { regex: "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$" } } |
not matches | Doesn't match regex pattern | { regex: string, flags?: string } | { field: "username", operator: "not matches", value: { regex: "^(admin|root)$", flags: "i" } } |
Date Operations
is before | Date is before specified date | string | number | Date | { field: "expiry", operator: "is before", value: new Date('2024-12-31') } |
is after | Date is after specified date | string | number | Date | { field: "startDate", operator: "is after", value: new Date('2024-01-01') } |
is on or before | Date is on or before specified date | string | number | Date | { field: "deadline", operator: "is on or before", value: new Date() } |
is on or after | Date is on or after specified date | string | number | Date | { field: "validFrom", operator: "is on or after", value: new Date() } |
Math & Number Validation
is even | Number is even | none | { field: "quantity", operator: "is even" } |
is odd | Number is odd | none | { field: "productId", operator: "is odd" } |
is positive | Number is positive (> 0) | none | { field: "balance", operator: "is positive" } |
is negative | Number is negative (< 0) | none | { field: "adjustment", operator: "is negative" } |
is empty | Value is null, undefined, or empty string/array | none | { field: "optionalField", operator: "is empty" } |
is not empty | Value is not empty | none | { field: "requiredField", operator: "is not empty" } |
Data Validation
is valid email | Valid email address | EmailValidationConfig (optional) | { field: "email", operator: "is valid email" } |
is valid phone | Valid phone number | PhoneValidationConfig | { field: "phone", operator: "is valid phone", value: { locale: "us" } } |
is URL | Valid URL | URLValidationConfig | { field: "website", operator: "is URL", value: { requireTld: false } } |
is UUID | Valid UUID | UUIDValidationConfig | { field: "id", operator: "is UUID", value: { version: 4 } } |
is EAN | Valid EAN barcode | none | { field: "barcode", operator: "is EAN" } |
is IMEI | Valid IMEI number | IMEIValidationConfig | { field: "deviceId", operator: "is IMEI", value: { allowHyphens: true } } |
is unit | Valid unit of measurement | UnitType | { field: "distance", operator: "is unit", value: "length" } |
is country | Valid country identifier | CountryValidationConfig | { field: "country", operator: "is country", value: { format: "iso2" } } |
is domain | Valid domain name | DomainValidationConfig | { field: "domain", operator: "is domain", value: { requireTld: true } } |
Rule Structure
Basic Rule
interface Rule {
conditions: Condition | Condition[];
default?: any;
}
Conditions
Rules support three logical operators:
all | AND | All constraints must be true |
any | OR | At least one constraint must be true |
none | NOT | No constraints should be true |
Constraints
{
field: string,
operator: string,
value: any
}
Examples
Basic Example
const rule: Rule = {
conditions: {
all: [
{ field: "age", operator: "is greater than or equal", value: 21 },
{ field: "country", operator: "in", value: ["US", "CA"] }
]
}
};
const user = { age: 25, country: "US" };
const result = await JsonRules.evaluate(rule, user);
Complex Conditions
const complexRule: Rule = {
conditions: {
any: [
{
all: [
{ field: "membershipTier", operator: "is equal", value: "premium" },
{ field: "accountAge", operator: "is greater than", value: 365 }
]
},
{
all: [
{ field: "totalSpent", operator: "is greater than", value: 1000 },
{ field: "lastPurchase", operator: "is after", value: new Date('2024-01-01') }
]
}
]
}
};
Validation Operators
const validationRule: Rule = {
conditions: {
all: [
{ field: "email", operator: "is valid email", value: null },
{ field: "phone", operator: "is valid phone", value: { locale: "us" } },
{ field: "website", operator: "is URL", value: { protocols: ["https"] } }
]
}
};
Template Variables
const dynamicRule: Rule = {
conditions: {
all: [
{ field: "endDate", operator: "is after", value: "{startDate}" },
{ field: "price", operator: "is less than", value: "{maxBudget}" }
]
}
};
API Reference
JsonRules.evaluate()
static async evaluate<T>(
rule: Rule,
criteria: object | object[],
trustRule?: boolean
): Promise<T | boolean>
JsonRules.validate()
static validate(rule: Rule): ValidationResult
ValidationResult
interface ValidationResult {
isValid: boolean;
error?: ValidationError;
}
Phone Number Validation
To use phone validation, import the specific locale validators:
import "@ivandt/json-rules/validators/phone/us";
import "@ivandt/json-rules/validators/phone/gb";
import "@ivandt/json-rules/validators/phone/de";
const rule: Rule = {
conditions: {
all: [
{ field: "phone", operator: "is valid phone", value: { locale: "us" } }
]
}
};
Contributing
- Fork the repository
- Create a feature branch:
git checkout -b feature/new-feature
- Make your changes with tests
- Run the test suite:
npm test
- Submit a pull request
License
MIT License - see LICENSE file for details.