Rules Engine TS is a strongly typed rules engine for evaluating deep and complex rules. With the power of TypeScript, you can create type-safe rules that are easy to read and maintain.
The motivation for this project was to create an out-of-the-box solution that had a simple data structure that was easy to mutate and easy to persist in a database. The general intended use case was to build the rules engine using a user interface, write the rules into a database and in the future retrieve the rules back from the database and run the rules against a target object.
The recommended way to consume rules-engine-ts
is in a TypeScript environment. TypeScript will warn you when your rules are missing properties or if the types of your properties are incorrect. That isn't to say that rules-engine-ts
can't be run with JavaScript. You will still get autocomplete on the available properties, but you will not get any warnings if you are missing properties or if the types of your properties are incorrect.
A rules engine can be configured and run like so:
1import { addRuleToUnion, addRulesToUnion, addUnionToUnion, createRoot, run } from 'rules-engine-ts';
2
3// Create root union
4const root = createRoot({ connector: 'and' });
5
6// Add a rule to the root union
7addRuleToUnion(root, { type: 'number', field: 'age', operator: 'greater_than', value: 18 });
8
9// Add a union to the root union (creates a nested ruleset)
10const union = addUnionToUnion(root, { connector: 'or' });
11
12// Add nested rules to the nested union
13addRulesToUnion(union, [
14 { type: 'string', field: 'name', value: 'bob', operator: 'equals_to', ignore_case: true },
15 { type: 'string', field: 'name', value: 'alice', operator: 'equals_to', ignore_case: true },
16]);
17
18// Run the rules engine
19const pass = run(root, { age: 19, name: 'Bob' });
20const fail = run(root, { age: 19, name: 'Carol' });
21
22console.log(pass); // true
23console.log(fail); // false
If we console log the root we can see what our rules look like:
1{
2 "entity": "root_union",
3 "id": "0d7428af-10e4-481b-84a7-056946bd4f12",
4 "connector": "and",
5 "rules": [
6 {
7 "entity": "rule",
8 "id": "82e96b0d-886e-4a2e-bf8c-f81b02ef11ce",
9 "parent_id": "0d7428af-10e4-481b-84a7-056946bd4f12",
10 "type": "number",
11 "field": "age",
12 "operator": "greater_than",
13 "value": 18
14 },
15 {
16 "entity": "union",
17 "id": "7c493486-409b-48df-bd66-7f4a16500c5e",
18 "parent_id": "0d7428af-10e4-481b-84a7-056946bd4f12",
19 "connector": "or",
20 "rules": [
21 {
22 "entity": "rule",
23 "id": "3abc4e64-d6c8-4303-9d07-b573a571f19a",
24 "parent_id": "7c493486-409b-48df-bd66-7f4a16500c5e",
25 "type": "string",
26 "field": "name",
27 "operator": "equals_to",
28 "value": "bob",
29 "ignore_case": true
30 },
31 {
32 "entity": "rule",
33 "id": "a3995445-55ca-49b2-8381-3d6758750413",
34 "parent_id": "7c493486-409b-48df-bd66-7f4a16500c5e",
35 "type": "string",
36 "field": "name",
37 "operator": "equals_to",
38 "value": "alice",
39 "ignore_case": true
40 }
41 ]
42 }
43 ]
44}
The rules can then be persisted into a database in JSON format:
1{
2 "entity": "root_union",
3 "id": "598444ae-032c-4ae5-85da-644cf90ab920",
4 "connector": "or",
5 "rules": [
6 {
7 "entity": "rule",
8 "id": "03fcb9b5-a3fe-4d63-97f3-dfce431c331d",
9 "parent_id": "598444ae-032c-4ae5-85da-644cf90ab920",
10 "type": "string",
11 "field": "user_display_name",
12 "operator": "equals_to",
13 "value": "Alice",
14 "ignore_case": true
15 },
16 {
17 "id": "d60639aa-8239-40c7-9cc3-ec89f8f8c58d",
18 "entity": "union",
19 "connector": "and",
20 "parent_id": "598444ae-032c-4ae5-85da-644cf90ab920",
21 "rules": [
22 {
23 "entity": "rule",
24 "id": "1821d9da-9f37-4689-a118-bf436ca37e89",
25 "parent_id": "d60639aa-8239-40c7-9cc3-ec89f8f8c58d",
26 "type": "string",
27 "field": "user_display_name",
28 "operator": "equals_to",
29 "value": "Bob",
30 "ignore_case": true
31 },
32 {
33 "entity": "rule",
34 "id": "c2a058ab-6005-44a4-94ae-b75736dce536",
35 "parent_id": "d60639aa-8239-40c7-9cc3-ec89f8f8c58d",
36 "type": "number",
37 "field": "total_challenges",
38 "operator": "greater_than_or_equal_to",
39 "value": 5
40 }
41 ]
42 }
43 ]
44}
At a later date, the rules can retrieved from the database and can be run by the rules engine like this:
1import { run } from 'rules-engine-ts';
2
3const rules = getRulesFromDatabase();
4
5const pass = run(rules, { user_display_name: 'alice', total_challenges: 0 });
6const fail = run(rules, { user_display_name: 'bob', total_challenges: 0 });
7
8if (pass) {
9 // do something
10}
11
12if (fail) {
13 //do somehting else
14}
15
The project is Open Source and can be viewed on GitHub & NPM.