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. Not eight reimplementations that drift apart: one Rust core under every binding, evaluating in nanoseconds. Store rules as JSON, change pricing, eligibility, and flag logic in production, and never redeploy to do it.
Build, trace, and debug rules live in the playground.
- π 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 1,565-case conformance battery in CI.
- π 100% sandbox-safe: 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.
Rules are plain JSON, so there is exactly one of them, no matter how many languages you run:
Rule: {"and": [{">=": [{"var": "age"}, 18]}, {"==": [{"var": "status"}, "active"]}]}
Data: {"age": 25, "status": "active"}
Result: trueThe same evaluation, one line in each runtime:
| Runtime | One-shot evaluation |
|---|---|
| Rust | datalogic_rs::eval_str(rule, data)? |
| Node.js | apply(rule, data) β @goplasmatic/datalogic-node |
| Browser / Edge (WASM) | evaluate(rule, data, false) β @goplasmatic/datalogic-wasm |
| Python | apply(rule, data) β datalogic_py |
| Go | datalogic.Apply(rule, data) |
| Java / Kotlin | engine.apply(rule, data) |
| .NET (C#) | engine.Apply(rule, data) |
| PHP | $engine->apply($rule, $data) |
Same bytes in, same bytes out: every binding wraps the same core and passes the same 54-suite conformance battery. Each package README has the full quickstart for its language, and every binding ships the same three runnable programs under its examples/ folder β the folders themselves are the parity demo.
| Language / Environment | Version | Package | Install | Guide |
|---|---|---|---|---|
| Rust | datalogic-rs |
cargo add datalogic-rs |
crate README | |
| Node.js (native prebuilds) | @goplasmatic/datalogic-node |
npm i @goplasmatic/datalogic-node |
node README | |
| Browser, Edge, Bun, Deno | @goplasmatic/datalogic-wasm |
npm i @goplasmatic/datalogic-wasm |
wasm README | |
| Python | datalogic-py |
pip install datalogic-py |
python README | |
| Go | datalogic-go |
go get github.com/GoPlasmatic/datalogic-rs/bindings/go/v5 |
go README | |
| Java / JVM (Kotlin, Scala) | io.github.goplasmatic:datalogic |
Maven / Gradle dependency | jvm README | |
| .NET (C#, F#) | Goplasmatic.Datalogic |
dotnet add package Goplasmatic.Datalogic |
dotnet README | |
| PHP | goplasmatic/datalogic |
composer require goplasmatic/datalogic |
php README | |
| C / FFI (embed anywhere) | built in-tree | datalogic-c |
built locally | c README |
| React visual editor | @goplasmatic/datalogic-ui |
npm i @goplasmatic/datalogic-ui |
ui README |
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"Enable templating mode and JSON key-value structures flow through to the output, with operators computing fields 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 (in Rust: Engine::builder().with_templating(true), behind the templating feature).
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.5Try any of these live in the playground, or browse the use-case cookbook for feature flags, fraud scoring, and data transformation recipes.
For admin portals and dashboards where non-engineers author rules, drop @goplasmatic/datalogic-ui into your React app. It runs the WASM core internally to compile and trace execution 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)}
/>Every binding exposes the same seven patterns, so knowledge transfers across 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/Number/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 |
Rust adds two more tiers: zero-copy evaluation into a caller-owned arena, and traced evaluation powering the visual debugger. See the Rust crate deep-dive for the full ladder.
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: cargo run --release -p datalogic-bench --bin compare β full matrix and caveats in tools/benchmark/BENCHMARK.md.
- Conformance, enforced in CI β passes the official JSONLogic suite plus an extended cross-binding battery: 1,565 cases across 54 suites, run against the same core every binding ships.
- 59 built-in operators β comparison, arithmetic, logic, strings, arrays, datetime, error handling; extensible with custom operators authored per host language.
- Thread-safe evaluation β compiled
LogicisSend + Sync; share it across threads viaArc. - Zero
unsafeβ the core engine forbids unsafe code (#![forbid(unsafe_code)]). - Zero-copy variables β
bumpalo-backed evaluation; read-through operations likevarborrow directly from the input. - Serde-optional β the default build has no
serde_jsondependency; enable the feature only for typed interop. - Configurable semantics β division-by-zero behavior, NaN handling, truthiness rules, and numeric coercions are all engine options.
- Verifiable supply chain β npm packages publish from GitHub Actions with provenance attestation; check with
npm audit signatures.
The opt-in flagd cargo feature (enabled in every language binding) ships the fractional and sem_ver operators used by OpenFeature flagd flag definitions. fractional implements murmurhash3 bucketing byte-compatible with the canonical Go evaluator, so users land in the same variant buckets across implementations. That makes the engine usable as an in-process, flagd-compatible feature-flag evaluator in all eight runtimes.
v5 contains breaking API updates: DataLogic is renamed 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 guide.
- Documentation site β operator reference, per-language guides, configuration
- Online playground β build and debug rules in your browser
- How it compares β vs json-logic-js, json-logic-engine, jsonlogic-rs, ZEN, CEL
- Rust API docs on docs.rs
- JSONLogic specification
- Architecture overview Β· Development guide Β· Changelog
- dataflow-rs (Plasmatic) β workflow/rules automation engine; every route condition is a compiled datalogic rule.
- datafake-rs (Plasmatic) β mock JSON data generator configured with JSONLogic expressions.
Running datalogic-rs in production? Add your project β a one-line PR or issue is enough.
See CONTRIBUTING.md for contribution rules, DEVELOPMENT.md for environment setup, and ARCHITECTURE.md for structural diagrams. Questions and ideas are welcome in Discussions.
Created by Plasmatic, building open-source tools for financial infrastructure and data processing.
Licensed under Apache 2.0. See LICENSE for details.