sparq
Back to Capabilities

JavaScript / WASM

The @jeswr/sparq RDF/JS API — the Rust engine compiled to a single ~886 KB wasm artifact.

Live in your tab

Try it: drive the API in your tab

Engine loading…

One seeded Store of six people; each panel below calls a different part of the @jeswr/sparq surface against it — the real Rust engine, compiled to wasm, running here.

Streaming cursor — queryCursor()

Pull the result in self-contained batches of batchSize rows — the consumer never holds more than one batch at a time. Lower the batch size to watch more pulls arrive.

2

RDF/JS lookup — match() / countQuads()

A triple-pattern lookup: leave a position blank for a wildcard, or paste an N-Triples term (<iri> or "literal"). countQuads() reads the count from the index without materialising the terms.

Solution count — count()

Count a SELECT's solutions without building any JS-side Bindings — read straight from the index where possible. Query: SELECT * WHERE { ?p foaf:knows ?o }

Incremental delta — applyDelta()

Mutate the store in place with a quad-level delta — O(batch), no index rebuild. Watch the size and person count change; the lookup and count panels above re-derive from the same live store.

triples foaf:Person
Import Dataset from a <script type="module">
The named Dataset export is an RDF/JS DatasetCore over the engine — importable directly in a browser module script from this project's self-hosted bundle (no third-party CDN). The async factories (Dataset.fromString / .create / .fromQuads) instantiate the wasm engine on first use, so the ~MB binary loads lazily — never on import.
<script type="module">
  // Self-hosted: this project's OWN GitHub Pages origin — no third-party CDN. The
  // ~MB engine wasm is fetched LAZILY by the first `await Dataset.fromString(...)`
  // below — NOT by this import line — so it never blocks the page paint.
  import { Dataset, DataFactory as DF } from "https://sparq.jeswr.org/wasm/sparq.js";

  const ds = await Dataset.fromString(
    '<http://ex/a> <http://ex/name> "Alice" .',
    "ntriples",
  );
  console.log(ds.size); // 1  (DatasetCore: size, add, delete, has, match, iterate)

  ds.add(DF.quad(
    DF.namedNode("http://ex/b"), DF.namedNode("http://ex/name"), DF.literal("Bob"),
  ));
  for (const q of ds.match(null, DF.namedNode("http://ex/name"), null)) {
    console.log(q.subject.value, "→", q.object.value);
  }

  // Drop to the full SPARQL engine when DatasetCore is not enough:
  console.log(ds.store.queryBoolean("ASK { ?s ?p ?o }")); // true
  ds.free();
</script>

sparq.js is a single self-contained ESM file published into this site at /wasm/sparq.js — the engine .wasm stays out of it, fetched lazily by the first await. The same named entry is also on an ESM CDN (the @jeswr/sparq npm package):

<script type="module">
  // Alternative: the same named entry from an ESM CDN (the published @jeswr/sparq npm package).
  import { Dataset } from "https://esm.sh/@jeswr/sparq";
</script>

Prefer Dataset (or SparqStore) in an app — the cold start is memoised, so it is paid at most once per page. The lower-level engine handle is also importable: the wasm-pack glue is itself an ESM module.

<script type="module">
  // Low-level: the wasm-pack `--target web` glue is itself a real ESM module.
  import init, { Store } from "https://sparq.jeswr.org/wasm/sparq_wasm.js";
  await init(); // lazily fetches + instantiates sparq_wasm_bg.wasm
  const store = Store.load("<a> <b> <c> .", "ntriples");
</script>

@jeswr/sparq wraps the Rust triplestore + SPARQL engine — compiled to a single ~886 KB (≈314 KB gzipped) wasm artifact — in an idiomatic RDF/JS surface that runs unchanged in Node ≥ 18 and the browser; this very site uses it for every live demo, and the panels below run against one seeded store in your tab.

What it does

  • SparqStore (RDF/JS)fromString / fromCompressed, query() yielding Map-like Bindings, idiomatic terms, plus the raw Store class for SPARQL-JSON strings.
  • Dataset — RDF/JS DatasetCoreNamed ESM entry: add / delete / has / match / size / iterate, lazily wasm-initialised; .store drops to the full SPARQL surface.
  • Streaming cursorsIterate large SELECT results in batches without materialising the whole table.
  • RDF/JS match() / countQuads() / count()The standard Source interface, plus a solution count read from the index without building every binding.
  • applyDelta / SPARQL UpdateApply quad-level deltas or SPARQL Update to mutate the store in place.

This is the engine — every live demo on this site calls this API; in Node it loads the same wasm binary, and in the browser it streams it on first use, never on import.

Reproduce: npm i @jeswr/sparq

Caveats & limitations

The SparqStore.query() wrapper covers SELECT/ASK; CONSTRUCT / DESCRIBE come via queryQuads() (RDF/JS quads), queryQuadsString() (N-Triples) and queryQuadsStream() — drop to the raw Store only to skip term materialisation. The lean bundle omits REGEX/REPLACE and the wall-clock query budget (see the SPARQL surface), trading a smaller download for those native-only features.

Open the live REPL@jeswr/sparq on npmCrate READMESKILL.mdSource on GitHub