Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

RDF/JS in JavaScript

In JavaScript, PurRDF does not invent its own API shape: the npm package @blackcatinformatics/purrdf implements the RDF/JS community specifications — DataFactory, DatasetCore, and Stream/Sink — over the wasm-compiled native engine. Code written against RDF/JS interfaces works with PurRDF terms and datasets.

The data factory

DataFactory covers the standard RDF/JS term constructors — namedNode, blankNode, literal(value, languageOrDatatype?), variable, defaultGraph, quad, fromTerm, fromQuad — plus explicit RDF 1.2 extensions:

import { ready, DataFactory } from "@blackcatinformatics/purrdf";
await ready();
const f = new DataFactory();

// RDF/JS standard surface.
const q = f.quad(
  f.namedNode("https://ex/alice"),
  f.namedNode("https://ex/knows"),
  f.namedNode("https://ex/bob"),
);

// RDF 1.2 extensions: quoted triple terms and base-direction literals.
const quoted = f.quotedTriple(q.subject, q.predicate, q.object);
const rtl = f.directionalLiteral("مرحبا", "ar", "rtl");

typedLiteral is a convenience alongside the spec’s overloaded literal.

Datasets

Dataset implements RDF/JS DatasetCore: add, delete, has, match, size, and iteration (for (const quad of dataset)), plus parsing and serialization through the native codecs:

import { Dataset } from "@blackcatinformatics/purrdf";

const ds = Dataset.parse("<https://ex/s> <https://ex/p> <https://ex/o> .", "ntriples");
for (const quad of ds.match(null, f.namedNode("https://ex/p"), null)) {
  console.log(quad.subject.value);
}
const trig = ds.serialize("trig");

Accepted format names: turtle, ntriples, nquads, trig, rdfxml, trix, hextuples, jsonld, yamlld, or their media types. Because these are the native codecs, output is byte-deterministic and identical to what the Rust, Python, and C surfaces emit (Codecs & Determinism).

SPARQL

Use QueryEngine when running more than one query or when the caller wants typed results instead of raw strings. The engine owns the native SPARQL plan cache and returns package-root terms and datasets:

import { QueryEngine } from "@blackcatinformatics/purrdf";

const engine = new QueryEngine();
const result = engine.select(
  ds,
  "PREFIX ex: <https://ex/> SELECT ?o WHERE { ex:s ex:p ?o }",
);
console.log(result.rows.take(0)?.o?.value);

const graph = engine.construct(
  ds,
  "PREFIX ex: <https://ex/> CONSTRUCT { ex:copy ex:p ?o } WHERE { ex:s ex:p ?o }",
);

SELECT rows are single-owner and lazy across the wasm boundary. Iterate result.rows, call result.rows.take(index) for indexed consumption, or call result.rows.toArray() to materialize the remaining rows. A row can be consumed once; call result.free() when abandoning a result before exhaustion.

QueryEngine.queryRaw(...) serializes SELECT/ASK results as SPARQL Results JSON/XML/CSV/TSV and graph results through the same graph formats accepted by Dataset.serialize. QueryEngine.update(...) applies SPARQL UPDATE atomically: the dataset changes only after the whole update succeeds.

Streams and sinks

The package speaks the async RDF/JS Stream/Sink protocol over the purrdf-events ingestion seam:

  • Sink — a streaming consumer: push(quad) per quad, finish() returns the accumulated Dataset.
  • datasetToStream(dataset) / streamToDataset(stream) — bridge between a Dataset and RDF/JS streams, for piping into or out of other RDF/JS tooling.

Scope notes

  • The package’s Dataset holds data in memory; these SPARQL methods query that resident dataset. The synchronous methods install no SERVICE or LOAD source, so there a remote SERVICE or LOAD fails explicitly unless written SILENT; the asynchronous twins reach remote endpoints only through the handlers the host passes them (see Asynchronous queries and federation).
  • A quoted-triple term as a quad object round-trips through Turtle, N-Triples, N-Quads, TriG, RDF/XML and JSON-LD / YAML-LD; TriX and HexTuples have no triple-term surface, and serializing one to them throws (see Scope and current limitations).