| Statements | Functions | Lines |
|---|---|---|
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.
- 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
- 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
npm install @ivandt/json-rulesThis library has one dependency:
validator- Used for advanced validation operators (email, URL, phone numbers, etc.)
import { JsonRules, Rule } from "@ivandt/json-rules";
// Define a rule
const rule: Rule = {
conditions: {
all: [
{ field: "age", operator: "is greater than or equal", value: 18 },
{ field: "country", operator: "is equal", value: "US" }
]
}
};
// Evaluate against data
const user = { age: 25, country: "US" };
const result = JsonRules.evaluate(rule, user);
console.log(result); // trueJsonRules provides operators for common data operations:
| Operator | Description | Accepts | Example |
|---|---|---|---|
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 } |
| Operator | Description | Accepts | Example |
|---|---|---|---|
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] } |
| Operator | Description | Accepts | Example |
|---|---|---|---|
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" } |
| Operator | Description | Accepts | Example |
|---|---|---|---|
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" } |
| Operator | Description | Accepts | Example |
|---|---|---|---|
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" } } |
| Operator | Description | Accepts | Example |
|---|---|---|---|
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() } |
| Operator | Description | Accepts | Example |
|---|---|---|---|
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" } |
| Operator | Description | Accepts | Example |
|---|---|---|---|
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 } } |
interface Rule {
conditions: Condition | Condition[];
default?: any;
}Rules support three logical operators:
| Type | Logic | Description |
|---|---|---|
all |
AND | All constraints must be true |
any |
OR | At least one constraint must be true |
none |
NOT | No constraints should be true |
{
field: string, // Property path (supports dot notation)
operator: string, // Comparison operator
value: any // Expected value or template reference
}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 = JsonRules.evaluate(rule, user); // trueconst 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') }
]
}
]
}
};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"] } }
]
}
};const dynamicRule: Rule = {
conditions: {
all: [
{ field: "endDate", operator: "is after", value: "{startDate}" },
{ field: "price", operator: "is less than", value: "{maxBudget}" }
]
}
};static evaluate<T>(
rule: Rule,
criteria: object | object[],
trustRule?: boolean
): T | booleanstatic validate(rule: Rule): ValidationResultinterface ValidationResult {
isValid: boolean;
error?: ValidationError;
}To use phone validation, import the specific locale validators:
// Import specific locales
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" } }
]
}
};- 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
MIT License - see LICENSE file for details.