A fast, production-ready engine for JSONLogic — Rust core, Node-native, WASM, Python, Go, React debugger.
datalogic-rs is a fast Rust engine for JSONLogic,
a JSON-shaped language for evaluating logical rules against data. Use it
as a rule engine for business logic, a JSON template engine for
response shaping, or a safe expression evaluator for user-supplied
formulas — and run the same rules in Rust, Node.js (native via napi),
the browser (WebAssembly), Python, Go, or a React visual debugger.
Try it live in the online playground — no install required.
The Rust crate is the engine. Every other package wraps it for a specific runtime — same rules, same semantics, same operators. Click through to the binding's own README for install, quick start, and the full API reference for that language.
| Your stack | Package | Install | Deep-dive |
|---|---|---|---|
| Rust application or service | datalogic-rs |
cargo add datalogic-rs |
crates/datalogic-rs/README.md |
| Node.js service (TypeScript or JS) | @goplasmatic/datalogic-node |
npm i @goplasmatic/datalogic-node |
bindings/node/README.md |
| Browser, Deno, Bun, Cloudflare Workers, edge runtimes | @goplasmatic/datalogic-wasm (WebAssembly) |
npm i @goplasmatic/datalogic-wasm |
bindings/wasm/README.md |
| Python service or data pipeline | datalogic-py |
pip install datalogic-py |
bindings/python/README.md |
| Go service | datalogic-go |
go get github.com/GoPlasmatic/datalogic-rs/bindings/go/v5 |
bindings/go/README.md |
| Java / JVM service | io.github.goplasmatic:datalogic |
Maven Central dependency | bindings/jvm/README.md |
| .NET service | Goplasmatic.Datalogic |
dotnet add package Goplasmatic.Datalogic |
bindings/dotnet/README.md |
| PHP service | goplasmatic/datalogic |
composer require goplasmatic/datalogic |
bindings/php/README.md |
| React visual rule editor / debugger | @goplasmatic/datalogic-ui |
npm i @goplasmatic/datalogic-ui |
ui/README.md |
| C ABI for new language bindings | datalogic-c (in-tree) |
build locally — shared FFI surface for Go/JVM/.NET/PHP | bindings/c/README.md |
Not sure which one? If you're writing the rules and evaluating them in the same service, pick the binding for that service's language.
On Node.js, reach for @goplasmatic/datalogic-node — it's the
native build (per-platform .node prebuild via
napi-rs), which is materially faster than the WASM
path. The WASM package is the right pick when you need a single
artifact across browser + edge runtimes (Deno, Bun, Cloudflare Workers)
or when you'd rather avoid per-platform prebuilt binaries.
If you're building a UI that lets humans author rules, also pull in
@goplasmatic/datalogic-ui — it consumes the WASM
binding and gives you a visual editor and step-through debugger.
The full documentation site is the long-form home for everything below: the operator reference (all 59 built-ins, organised by category), advanced topics (templating, custom operators, tracing, configuration), and an interactive playground for trying rules live without installing anything. The rest of this README is the short version.
Encode access control, feature flags, and validation as JSON. Rules are data — store them in a database, send them over an API, change them without redeploys.
let result = datalogic_rs::eval_str(
r#"{"and": [{">=": [{"var": "age"}, 18]}, {"==": [{"var": "status"}, "active"]}]}"#,
r#"{"age": 25, "status": "active"}"#,
).unwrap();
assert_eq!(result, "true");Shape one JSON payload into another. With templating mode, object keys flow through to the output and operator values become computed fields — the template's structure mirrors the response you want.
// Cargo.toml: datalogic-rs = { version = "5", features = ["templating"] }
use datalogic_rs::Engine;
let engine = Engine::builder().with_templating(true).build();
let result = engine.eval_str(
r#"{"greeting": {"cat": ["Hello ", {"var": "name"}]},
"isAdult": {">=": [{"var": "age"}, 18]}}"#,
r#"{"name": "Jane", "age": 25}"#,
).unwrap();
// {"greeting":"Hello Jane","isAdult":true}Let users author formulas; evaluate them safely without eval().
Arithmetic, comparisons, and array reductions are all built in.
let result = datalogic_rs::eval_str(
r#"{"+": [{"var": "subtotal"}, {"var": "tax"}, {"var": "shipping"}]}"#,
r#"{"subtotal": 100, "tax": 8.5, "shipping": 5}"#,
).unwrap();
assert_eq!(result, "113.5");reduce, map, filter, and sort extend the same pattern to
aggregations over arrays.
The same JSONLogic rule runs unchanged across every supported runtime. Author the rule once; evaluate it on the server, in the browser, or inside a visual editor.
Rust — server-side, native:
let result = datalogic_rs::eval_str(
r#"{">": [{"var": "x"}, 10]}"#,
r#"{"x": 42}"#,
).unwrap();
// "true"Node.js (native) — services, scripts, CLIs:
import { apply } from '@goplasmatic/datalogic-node';
const result = apply({ '>': [{ var: 'x' }, 10] }, { x: 42 });
// trueBrowser / Deno / Bun / Cloudflare Workers — via WebAssembly:
import init, { evaluate } from '@goplasmatic/datalogic-wasm';
await init();
const result = evaluate('{">": [{"var": "x"}, 10]}', '{"x": 42}', false);
// "true"Python — services, scripts, data pipelines:
from datalogic_py import apply
result = apply({">": [{"var": "x"}, 10]}, {"x": 42})
# TrueGo — services, CLIs:
import datalogic "github.com/GoPlasmatic/datalogic-rs/bindings/go/v5"
out, _ := datalogic.Apply(`{">": [{"var": "x"}, 10]}`, `{"x": 42}`)
// "true"React — drop-in visual debugger / editor:
import { DataLogicEditor } from '@goplasmatic/datalogic-ui';
<DataLogicEditor
value={{ ">": [{ "var": "x" }, 10] }}
data={{ x: 42 }}
/>See the rule run live in your browser at the online playground.
Every binding exposes the same conceptual ladder — pick the entry point that matches how often you evaluate and how much control you want over allocation.
| What it is | Use when |
|---|---|
Tier 0 — Module-level one-shot — eval_str, eval, eval_into, compile |
Quick scripts, ad-hoc evaluation, no custom configuration |
Tier 1 — Engine one-shot — Engine::eval* |
You need custom operators, non-default config, or templating mode |
Tier 2 — Session (hot loop) — Engine::session() + Session::eval* |
You're evaluating compiled rules many times — services, batch jobs, request handlers |
Tier 3 — Zero-copy evaluate — Engine::evaluate(&Logic, data, &Bump) |
You want results that borrow directly into a caller-owned arena (specialised use) |
Tier 4 — Traced evaluation — Engine::trace() |
Debugging, visualising execution, building inspector UIs |
Most callers want Tier 0 or Tier 2. Tier 0 is the right default for trying something out; reach for Tier 2 once the same rule is being evaluated repeatedly. Bindings expose the same ladder under language-idiomatic names — see each binding's README for the exact call sites. For the Rust deep-dive, including code per tier and runnable examples, see crates/datalogic-rs/README.md.
- Cross-platform — same engine, same rules in Rust, Node.js (native), browsers + edge runtimes (WASM), Python, Go, and a React UI
- 59 built-in operators with full JSONLogic spec compliance — 57 in the default build plus 2 opt-in OpenFeature flagd-compatible operators (
fractional,sem_ver) behindfeatures = ["flagd"] - Compile once, evaluate millions of times —
LogicisSend + Sync; share viaArc - Zero
unsafe— built with#![forbid(unsafe_code)] - Arena-allocated evaluation —
bumpalo-backed; read-through ops borrow zero-copy from the input serde_jsonis optional — opt in only when you need the value boundary- Configurable — NaN handling, division-by-zero, truthiness modes, numeric coercion
- Custom operators via a simple trait — same idea exposed in every binding
- Visual debugger + execution tracing for diagnosing rules
datalogic-rs is built for repeated evaluation. Compiled rules
dispatch through a single OpCode enum (no string lookups), values
live in a bumpalo::Bump arena (no per-result heap allocation), and
read-through operators like var borrow zero-copy from the caller's
input.
Geomeans across 44 suites (Apple M2 Pro, macOS 26.3, Rust 1.93, Node 24;
median of 3 samples per cell, ~200 ms wall budget — see tools/benchmark/BENCHMARK.md
for the per-suite matrix, methodology, and caveats):
| Library | Config / setup | Geomean ns/op | vs dlrs:engine |
|---|---|---|---|
datalogic-rs (native Rust, this repo) |
precompiled — dlrs:engine |
9.7 | 1× |
json-logic-engine (TotalTechGeek, JS) |
compiled — json-logic-engine:compiled |
47.2 | 4.9× |
json-logic-engine (TotalTechGeek, JS) |
interpreted — json-logic-engine |
160.3 | 16.5× |
jsonlogic-rs (bestowinc, native Rust) |
default — jsonlogic-rs |
218.0 | 22.5× |
json-logic-js (jwadhams reference, JS) |
default — json-logic-js |
423.5 | 43.7× |
@goplasmatic/datalogic-wasm (WebAssembly, run in Node) |
compiled — dlrs:wasm:compiled |
855.6 | 88.2× |
The WASM row above measures the WebAssembly build running in Node — the
artifact you'd ship to browsers / Deno / Bun / Cloudflare Workers, not
the Node-native package. Node consumers should reach for
@goplasmatic/datalogic-node (per-platform
napi-rs prebuilds) for production workloads; it shares the same Rust
core as the dlrs:engine row above with only the napi boundary added,
so its ceiling sits much closer to native Rust than to WASM. Native-Node
benchmark numbers will land here once the suite is wired up against the
@goplasmatic/datalogic-node prebuild.
Numbers are macOS / Apple Silicon — Linux x86_64 will distribute differently. Quote ratios, not absolute ns/op, when citing.
v5 is a breaking release with a hard cliff: no compat feature, no
deprecated method shims inside the v5 crate. Headline changes:
DataLogic → Engine, CompiledLogic → Logic, Operator →
CustomOperator; one-shot evaluation is now eval_str (returns
String) or eval_into::<T> (returns a typed value); custom operators
receive pre-evaluated &DataValue<'a> args and an EvalContext;
operator registration is builder-only; serde_json lives behind the
serde_json feature. See MIGRATION.md for the full
v4 → v5 cookbook.
- Full Documentation — long-form guide, operator reference, advanced topics
- Online Playground — try rules live
- Rust API on docs.rs
- JSONLogic Specification
- Architecture overview — how the packages depend on each other
- Development guide — build, test, run, link
See CONTRIBUTING.md for the contribution workflow, DEVELOPMENT.md for local setup and per-package commands, and ARCHITECTURE.md for the cross-package design.
Created by Plasmatic, building open-source tools for financial infrastructure and data processing.
Licensed under Apache 2.0. See LICENSE for details.