Skip to content

About

A fast JSONLogic rules engine: one Rust core with official bindings for Node.js, Python, Go, Java, .NET, PHP, and the browser (WASM), plus a React visual debugger.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

93 stars

Watchers

4 watching

Forks

Plasmatic Logo

datalogic-rs

Business rules as data. One engine, every runtime.

Write a JSONLogic rule once and evaluate it with the exact same engine in Rust, Node.js, the browser (WASM), Python, Go, Java, .NET, and PHP. One Rust core sits under every binding, so the eight runtimes cannot drift apart, and a rule evaluates in nanoseconds. Store rules as JSON and change pricing, eligibility, and flag logic in production without a redeploy.

CI Release Conformance License: Apache 2.0

🚀 Try the Live Playground | 📖 Read the Documentation


JSONLogic Online Debugger Demo

Build, trace, and debug rules live in the playground.


Why datalogic-rs?

  • 🌐 One rule, every runtime: every binding runs the same compiled Rust core, so a rule evaluates with identical semantics on your backend, your edge workers, and your frontend. No cross-language drift, verified by a 2,128-case conformance battery in CI.
  • 🔒 Sandboxed: evaluate user-submitted rules and formulas without arbitrary code execution. No eval(), no scripting runtime, no I/O; the core forbids unsafe code.
  • ⚡ Nanosecond evaluation: rules compile to OpCode-dispatched programs that run in a reusable memory arena: 10.3 ns geomean, 7.0× the fastest JS engine, 83.6× the reference implementation.
  • 🛠️ Ready-made rule builder: ship a visual editor and step-through debugger to your product dashboard with the companion React component, instead of building rule UI from scratch.

One rule, every runtime

Rules are plain JSON, so you keep one copy of each rule, however many languages you run:

Rule:   {"and": [{">=": [{"var": "age"}, 18]}, {"==": [{"var": "status"}, "active"]}]}
Data:   {"age": 25, "status": "active"}
Result: true

The same evaluation, one line in each runtime:

Runtime One-shot evaluation
Rust datalogic_rs::eval_str(rule, data)?
Node.js apply(rule, data) from @goplasmatic/datalogic-node
Browser / Edge (WASM) new Engine().evalStr(rule, data) from @goplasmatic/datalogic-wasm
Python apply(rule, data) from datalogic_py
Go datalogic.Apply(rule, data)
Java / Kotlin engine.apply(rule, data)
.NET (C#) engine.Apply(rule, data)
PHP $engine->apply($rule, $data)

Every binding wraps the same core and passes the same 66-suite conformance battery, so the same input bytes produce the same output bytes. Each package README has the full quickstart for its language, and every binding ships the same three runnable programs under its examples/ folder, so the folders double as the parity demo.


Pick your package

Language / Environment Version Package Install Guide
Rust Crates.io datalogic-rs cargo add datalogic-rs crate README
Node.js (native prebuilds) npm @goplasmatic/datalogic-node npm i @goplasmatic/datalogic-node node README
Browser, Edge, Bun, Deno npm @goplasmatic/datalogic-wasm npm i @goplasmatic/datalogic-wasm wasm README
Python PyPI datalogic-py pip install datalogic-py python README
Go Go Reference datalogic-go go get github.com/GoPlasmatic/datalogic-rs/bindings/go/v5 go README
Java / JVM (Kotlin, Scala) Maven Central io.github.goplasmatic:datalogic Maven / Gradle dependency jvm README
.NET (C#, F#) NuGet Goplasmatic.Datalogic dotnet add package Goplasmatic.Datalogic dotnet README
PHP Packagist goplasmatic/datalogic composer require goplasmatic/datalogic php README
C / FFI (embed anywhere) n/a datalogic-c GitHub release tarball, or cargo build --release c README
React visual editor npm @goplasmatic/datalogic-ui npm i @goplasmatic/datalogic-ui ui README

Three things you can build

1. Dynamic business rules

Encode pricing logic, fee schedules, eligibility and underwriting rules, transaction risk scoring, payment routing, access control, or form validation as JSON. Store rules in a database column, fetch them from an API, review them in a diff: logic changes ship without a deploy.

Rule:   {"if": [
          {">": [{"var": "cart.total"}, 100]}, "free-shipping",
          {">": [{"var": "cart.total"}, 50]},  "flat-rate",
          "standard"
        ]}
Data:   {"cart": {"total": 127.5}}
Result: "free-shipping"

2. JSON response templates

Enable templating mode to pass JSON objects through to the output while evaluating embedded operators in place:

Template: {"greeting": {"cat": ["Hello ", {"var": "name"}]},
           "isAdult":  {">=": [{"var": "age"}, 18]}}
Data:     {"name": "Jane", "age": 25}
Output:   {"greeting": "Hello Jane", "isAdult": true}

Templating is an engine option in every binding (Engine::builder().with_templating(true) in Rust, behind the templating Cargo feature). You can also choose the mode per compile (compile_template / compile_strict in Rust, compileTemplate / compileStrict in JavaScript), allowing a single engine to validate conditions strictly and compile output templates. An optional escape prefix (.with_template_key_escape('$') in Rust, available in every binding) lets you emit output keys that match operator names—for example, {"$type": ...} outputs the literal key "type" instead of running the type operator. See Structured Objects & Templating.

3. Safe user expressions

Let power users and admins write formulas without handing them a scripting engine:

Rule:   {"+": [{"var": "subtotal"}, {"var": "tax"}, {"var": "shipping"}]}
Data:   {"subtotal": 100, "tax": 8.5, "shipping": 5}
Result: 113.5

Try any of these live in the playground, or browse the use-case cookbook for feature flags, fraud scoring, and data transformation recipes.


🎨 Visual rule builder and debugger

For admin portals and dashboards where non-engineers author rules, drop @goplasmatic/datalogic-ui into your React app. It bundles the WASM engine to compile and trace rules live, and it is the same component behind the online playground.

import { DataLogicEditor } from '@goplasmatic/datalogic-ui';

<DataLogicEditor
  value={{ ">": [{ "var": "x" }, 10] }}
  data={{ x: 42 }}
  onChange={(newRule) => console.log('Rules modified:', newRule)}
/>

One API shape, every binding

Every binding exposes the same patterns, so what you learn in one language carries over to the rest of your stack:

Pattern Shape Use when
One-shot apply(rule, data) ad-hoc evaluation, scripts, low volume
Engine construct with config / custom operators non-default semantics, extensions
Compile once engine.compile(rule) → evaluate many one rule, many payloads
Session engine.session() hot loops; reuses the internal arena across evaluations
Parse once DataHandle(json) → evaluate many rules against it one payload, many rules or repeat evaluations; skips the dominant per-call parse cost
Typed session.evaluateBool/Int/Float/Truthy(rule, handle) predicates and scalar results; no JSON decode on the way out
Batch session.evaluateBatch(rule, handles) / evaluateMany(rules, handle) many evaluations in one call, per-item errors that never fail the set
Check engine.check(rule) / engine.compileChecked(rule) validating user-authored rules before you store or run them; each diagnostic carries a JSON Pointer into the rule
Inspect rule.facts() / engine.operators() the data paths a rule reads and the operators it calls; the catalogue of built-ins for editors and allow-lists

Every binding also has traced evaluation (evaluateWithTrace), which powers the visual debugger. Rust adds zero-copy evaluation into a caller-owned arena. See the Rust crate deep-dive for the full ladder.


Performance

Geomean execution time across 51 benchmark suites (Apple M2 Pro; median of 3 samples; ratios are pairwise shared-suite geomeans; methodology in tools/benchmark/BENCHMARK.md):

datalogic-rs (native Rust)              | 10.3 ns  (■) 1x
json-logic-engine (JS, compiled)        | 63.3 ns  (■■■■■■) 7.0x
json-logic-engine (JS, interpreted)     | 234.8 ns (■■■■■■■■■■■■■■■■■■■■■■■) 25.8x
jsonlogic-rs (bestowinc Rust engine)    | 264.2 ns (■■■■■■■■■■■■■■■■■■■■■■■■■■) 28.1x
json-logic-js (Reference JS library)    | 465.1 ns (■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■) 83.6x

Rules compile to a simple AST with OpCode dispatch (no runtime string matching) and execute inside a reusable memory arena: single-digit nanoseconds for folded rules, 10-120 ns for context-dependent ones.

In Node.js, the native @goplasmatic/datalogic-node package is the fast path and runs close to native Rust. The WASM build trades speed for portability (900.5 ns geomean under Node, 88× native, but it runs anywhere JavaScript does). Use native on Node servers; use WASM in browsers, edge runtimes, Deno, and Bun.

Reproduce it yourself with cargo run --release -p datalogic-bench --bin compare. The full matrix and caveats are in tools/benchmark/BENCHMARK.md.


Engine guarantees

  • Conformance, enforced in CI: passes the official JSONLogic suite plus an extended cross-binding battery: 2,128 cases across 66 suites, run against the same core every binding ships.
  • 84 built-in operators: comparison, arithmetic, logic, strings, arrays, objects, datetime (with IANA timezones), error handling, tensor marshalling, and the flagd operators. You can also register custom operators written in each host language. engine.operators() lists all active built-in operators from the same table that drives dispatch.
  • Checks before evaluation: check reports every static issue in a rule (unknown operators, invalid argument counts, or unknown timezones) along with an RFC 6901 JSON Pointer into the rule. facts reports the data paths and operators a compiled rule uses.
  • Operation budgets: bound untrusted rules with an operation budget (ops_budget) and inspect exact per-run consumption (evaluate_metered_report / evaluateMetered).
  • Thread-safe evaluation: compiled Logic is Send + Sync and can be shared across threads via Arc.
  • Zero unsafe: the core engine forbids unsafe code (#![forbid(unsafe_code)]).
  • Zero-copy variables: bumpalo-backed evaluation borrows read-through operations like var directly from the input. When a rule's data reads are statically known, input projection views only those paths from an owned or serde_json input.
  • Serde-optional: the default Rust build has no serde_json dependency. Enable the serde_json feature only when you want typed Rust interop.
  • Configurable semantics: division-by-zero behavior, NaN handling, truthiness rules, numeric coercions, operator-family allowlists, and missing-variable behavior (missing_var: return null or raise an error) are all configurable on the engine.
  • Verifiable supply chain: npm packages publish from GitHub Actions with provenance attestation (verify with npm audit signatures).

OpenFeature / flagd

The opt-in flagd Cargo feature (enabled by default in every language binding) provides the fractional and sem_ver operators used by OpenFeature flagd flag definitions. fractional implements MurmurHash3 bucketing that is byte-compatible with the canonical Go evaluator, so users land in the exact same variant buckets across implementations. This lets datalogic-rs act as an in-process, flagd-compatible feature-flag evaluator across all eight runtimes.


Migrating from v4

v5 introduces a cleaner API: it renames DataLogic to Engine, CompiledLogic to Logic, and Operator to CustomOperator. One-shot evaluation now uses eval_str (returning a String) or eval_into::<T> (for typed values). The npm WASM package moved from @goplasmatic/datalogic to @goplasmatic/datalogic-wasm. See MIGRATION.md for the step-by-step v4 → v5 guide as well as notes on behavior changes and deprecations across 5.x releases.


Resources


Who is using datalogic-rs?

  • dataflow-rs (Plasmatic): workflow and rules automation engine where every route condition is a compiled datalogic-rs rule.
  • datafake-rs (Plasmatic): mock JSON data generator configured with JSONLogic expressions.

If you run datalogic-rs in production, add your project—a one-line PR or issue is enough.


Contributing

See CONTRIBUTING.md for contribution guidelines, DEVELOPMENT.md for environment setup and build commands, ARCHITECTURE.md for structural diagrams, and CODE_OF_CONDUCT.md for community standards. Bring questions and ideas to Discussions.

About Plasmatic

Created by Plasmatic, building open-source tools for financial infrastructure and data processing.

License

Licensed under Apache 2.0. See LICENSE for details.

About

A fast JSONLogic rules engine: one Rust core with official bindings for Node.js, Python, Go, Java, .NET, PHP, and the browser (WASM), plus a React visual debugger.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

93 stars

Watchers

4 watching

Forks

Releases

Used by

Contributors

Languages