json-rules-engine

A lightweight, JSON-based rules engine for expressing and evaluating conditional business logic in Node.js and the browser.

Library
npm
v7.3.1
3,126stars
ISC

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
51/100Fair
Development Activity4
Maintenance20
Community80
Maturity60
Momentum40

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
74/100Good
Architecture80
Code Quality82
Innovation55
Learning Curve80

json-rules-engine is a rules engine that lets you express business logic as declarative JSON rather than nested if/else code. Rules are built from conditions (using all/any/not boolean grouping, with recursive nesting) and facts, which can be constant values or asynchronously computed at runtime; results are cached per engine run and reused across every condition that needs them, avoiding duplicate API calls or expensive recomputation.

Because rules are plain JSON, they’re easy to persist to a database, load dynamically at runtime, or edit through a rule-builder UI without redeploying code. The engine ships with common comparison operators (equal, in, contains, greaterThan, etc.), supports registering custom operators and operator decorators, and evaluates rules in priority order, emitting success/failure events for each one.

What You Get

  • Declarative rule format - conditions and events are plain JSON objects, so rules can be persisted, generated, or edited outside your codebase.
  • Async fact resolution with caching - facts can be computed via async functions, and results are cached per engine run so shared facts are only computed once.
  • Boolean condition composition - all, any, and not operators nest recursively for arbitrarily complex rule trees.
  • Extensible operators - built-in comparison operators (equal, in, contains, greaterThan, etc.) plus support for registering custom operators and operator decorators.
  • Priority-based rule execution - rules run in priority order, in parallel within each priority tier, with success/failure events for every rule.

Common Use Cases

  • Feature eligibility checks - product teams gate features or promotions behind JSON conditions (account tier, region, usage) editable without a deploy.
  • Fraud/risk scoring pipelines - risk teams compose layered rules over async-fetched account data to flag suspicious activity.
  • Dynamic pricing and discounting - e-commerce systems evaluate cart/customer facts against JSON rules to apply discounts or surcharges.
  • Workflow automation triggers - backend systems use rule matches to fire downstream events (notifications, state transitions) based on changing data.

Under The Hood

Architecture The engine (src/engine.js) holds a Map of Facts, a Map of named reusable Conditions, and an array of Rules. On run(), it creates an Almanac (src/almanac.js) — a per-run lookup that caches fact computations by cache key and resolves JSONPath sub-properties via jsonpath-plus — seeds it with constant and runtime facts, groups rules into priority tiers via prioritizeRules(), and resolves each tier’s rules in parallel through evaluateRules() before advancing to the next tier, emitting success/failure events (the engine extends eventemitter2) as each Rule resolves. This layering cleanly separates rule orchestration (Engine) from fact caching/lookup (Almanac) from condition evaluation (Condition/Operator), so changing how facts are cached or how conditions nest doesn’t require touching the engine’s run loop.

Tech Stack The package targets Node >=18 and is written in pre-ES2015+ syntax transpiled via Babel (babel-cli/babel-preset-es2015/babel-preset-stage-0) into a published dist/ bundle, with hand-written TypeScript declarations (types/index.d.ts) validated separately via tsd. Runtime dependencies are intentionally minimal: eventemitter2 for the engine’s event bus, jsonpath-plus for the default path resolver, hash-it for cache-key hashing, and clone for deep-cloning rule/condition definitions. There’s no database, ORM, or web framework involved — this is a pure, embeddable logic library.

Code Quality Testing uses Mocha with Chai/chai-as-promised/dirty-chai assertions and Sinon for stubs, with a comprehensive suite (33 test files) covering engine facts, conditions, events, error handling, custom properties, and rule serialization, plus a dedicated performance test. Linting is enforced via standard (with babel-eslint as parser) and wired into both npm test and CI. GitHub Actions runs the full lint+test suite across Node 18/20/22 on every push, and TypeScript types are exercised by a tsd-driven type test file, giving the project multi-layered quality gates for a library with no runtime type system of its own.

API Design The public API is small and consistent: new Engine(rules, options), engine.addRule(), engine.addFact(), engine.addOperator(), and engine.run(facts) cover the vast majority of usage, and the README’s basic example gets a working engine running in under 15 lines. Facts and conditions share the same JSON shape whether defined statically or computed at runtime, so there’s little conceptual overhead switching between the two, and errors are raised as typed classes (UndefinedFactError) rather than generic exceptions, making failure modes easy to catch and handle explicitly.

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search