Skip to content

Quad Runtime

Fresh

The Quad Runtime concept describes a paradigm shift: instead of querying a database with a query language, you work with a plain JavaScript array of RDF quads, using standard JavaScript as your graph traversal and transformation language.

The Core Insight

Text → Quad[] → Text

Everything flows through this loop. The document is the source. The array is the runtime. The text output is the result. No database engine, no query processor, no ORM, no schema migration, no connection string.

Quad[] as a Universal Semantic Runtime

An RDF quad is an object with four fields:

javascript
{
  subject:   { termType: 'NamedNode', value: 'http://example.org/alice' },
  predicate: { termType: 'NamedNode', value: 'http://xmlns.com/foaf/0.1/knows' },
  object:    { termType: 'NamedNode', value: 'http://example.org/bob' },
  graph:     { termType: 'DefaultGraph', value: '' }
}

An array of these is already a graph. JavaScript already knows how to work with arrays. No translation layer needed.

JavaScript as the Query Language

Replace SQL/SPARQL with standard JavaScript:

javascript
// SPARQL: SELECT ?person WHERE { ?person rdf:type prov:Person }
const persons = quads
  .filter(q =>
    q.predicate.value === 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' &&
    q.object.value === 'http://www.w3.org/ns/prov#Person'
  )
  .map(q => q.subject.value);

// SPARQL: SELECT ?email WHERE { ex:alice ex:email ?email }
const aliceEmails = quads
  .filter(q =>
    q.subject.value === 'http://example.org/alice' &&
    q.predicate.value === 'http://example.org/email'
  )
  .map(q => q.object.value);

The query syntax is the same JavaScript you already know. No new language. No parser. No optimizer. Direct array operations.

Building Indexes On Demand

When you need performance, build a Map:

javascript
// Build subject index: IRI → array of quads about that subject
const bySubject = new Map();
for (const quad of quads) {
  const key = quad.subject.value;
  if (!bySubject.has(key)) bySubject.set(key, []);
  bySubject.get(key).push(quad);
}

// Now O(1) lookup
const aliceQuads = bySubject.get('http://example.org/alice');

Build any index the problem requires. Discard it when done. No schema declaration required.

Graph Traversal in Plain JavaScript

Follow relationships across the graph:

javascript
function getProperties(quads, iri) {
  return quads
    .filter(q => q.subject.value === iri)
    .reduce((acc, q) => {
      const key = q.predicate.value.split(/[#/]/).pop();
      acc[key] = q.object.value;
      return acc;
    }, { iri });
}

function follow(quads, startIRI, predicateIRI) {
  return quads
    .filter(q =>
      q.subject.value === startIRI &&
      q.predicate.value === predicateIRI
    )
    .map(q => q.object.value);
}

// Who does Alice know?
const aliceKnows = follow(quads, 'http://example.org/alice', 'http://example.org/knows');

// Get full profile for each person Alice knows
const profiles = aliceKnows.map(iri => getProperties(quads, iri));

Integration with the Entire JS Ecosystem

Because quads are plain JS objects in a plain array, they integrate with everything:

javascript
// Filter by type
import { quads } from './parsed-document.js';
const activities = quads.filter(q =>
  q.predicate.value.endsWith('#type') &&
  q.object.value.endsWith('#Activity')
);

// Serialize to Turtle using n3.js
import { Writer } from 'n3';
const writer = new Writer({ prefixes: context });
writer.addQuads(quads);
writer.end((error, result) => console.log(result));

// Query with sparqljs
import { translate } from 'sparqljs';
const query = translate('SELECT * WHERE { ?s ?p ?o }');
// Apply query to quads

// Validate with shacl-engine
import SHACLEngine from 'shacl-engine';
const report = SHACLEngine.validate(quads, shapesQuads);

The Document-First Architecture

The MD-LD approach inverts the typical database-first architecture:

Database-first (traditional):

  1. Design schema
  2. Configure database
  3. Write ORM models
  4. Write queries
  5. Convert results to objects
  6. Render to users

Document-first (MD-LD):

  1. Write documents
  2. Parse to quads
  3. Operate on quads with JavaScript
  4. Generate back to documents

The document is the schema, the storage, and the display format. The array is the database, the ORM, and the query engine.

Local-First by Default

Because quads live in memory as JavaScript objects:

  • No network round-trips for queries
  • No connection management
  • No authentication to a database
  • No serialization/deserialization overhead

Everything runs locally, in the same JavaScript runtime as the rest of your application.

Merge as Database JOIN

javascript
import { merge } from 'mdld-parse';

// Combine multiple knowledge documents
const { quads } = merge([doc1, doc2, doc3]);

// The merged quads are a single array
// Retractions are resolved automatically
// Duplicates (same triple from multiple docs) are deduplicated

The merge() function replaces database JOINs for the common case of combining knowledge from multiple sources. It handles polarity (retractions) automatically.

Streaming and Workers

For large quad sets, use Web Workers or Node.js worker_threads:

javascript
// In a worker
import { parse } from 'mdld-parse';

self.onmessage = ({ data: { text } }) => {
  const { quads, primary } = parse({ text });
  self.postMessage({ quads, primary });
};

The quads are transferable objects — they can cross the thread boundary without copying.

When to Use a Real Triple Store

The Quad Runtime is designed for:

  • Single-file or small-collection knowledge bases (under ~50K quads in memory)
  • Local-first applications
  • Applications where the document is the primary artifact
  • LLM agent contexts where knowledge needs to be in the prompt

For truly large-scale graph databases (millions of triples, complex SPARQL queries, federation), use n3.js with a real triple store. The quads produced by MD-LD are RDF/JS compatible and work with any standard RDF tooling.