Skip to main content

rdflib.js

rdflib.js is the foundation of SolidOS — a JavaScript library for working with RDF (Resource Description Framework) data.

Installation

npm install rdflib

Or via CDN:

<script src="https://cdn.jsdelivr.net/npm/rdflib/dist/rdflib.min.js"></script>

Core Concepts

The Store (IndexedFormula)

The store is an in-memory RDF graph database:

import { graph } from 'rdflib'

const store = graph()

// The store holds triples (subject, predicate, object, graph)
// Also called "quads" when including the graph component

Nodes

RDF uses different node types:

import { sym, lit, blankNode, variable } from 'rdflib'

// NamedNode - a URI reference
const alice = sym('https://alice.example/profile/card#me')

// Literal - a value with optional language or datatype
const name = lit('Alice')
const age = lit(30, null, sym('http://www.w3.org/2001/XMLSchema#integer'))
const greeting = lit('Bonjour', 'fr')

// BlankNode - anonymous node
const address = blankNode()

// Variable - for queries
const x = variable('x')

Namespaces

Create namespace helpers for common vocabularies:

import { Namespace } from 'rdflib'

const FOAF = Namespace('http://xmlns.com/foaf/0.1/')
const VCARD = Namespace('http://www.w3.org/2006/vcard/ns#')
const RDF = Namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#')
const RDFS = Namespace('http://www.w3.org/2000/01/rdf-schema#')
const LDP = Namespace('http://www.w3.org/ns/ldp#')
const SOLID = Namespace('http://www.w3.org/ns/solid/terms#')

// Usage
const nameProperty = FOAF('name') // http://xmlns.com/foaf/0.1/name
const Person = FOAF('Person') // http://xmlns.com/foaf/0.1/Person

Working with the Store

Adding Data

import { graph, sym, lit, st } from 'rdflib'

const store = graph()
const alice = sym('https://alice.example/profile/card#me')
const doc = sym('https://alice.example/profile/card')

// Add a single statement
store.add(alice, FOAF('name'), lit('Alice'), doc)

// Using st() helper
const statement = st(alice, FOAF('age'), lit(30), doc)
store.add(statement)

// Add multiple statements
store.add(alice, RDF('type'), FOAF('Person'), doc)
store.add(alice, FOAF('knows'), sym('https://bob.example/#me'), doc)

Querying Data

// Get a single value
const name = store.any(alice, FOAF('name'), null, null)
console.log(name?.value) // "Alice"

// Get all values for a predicate
const friends = store.each(alice, FOAF('knows'), null, null)
friends.forEach(friend => console.log(friend.uri))

// Check if a statement exists
const isPerson = store.holds(alice, RDF('type'), FOAF('Person'), null)

// Get all statements matching a pattern
const statements = store.match(alice, null, null, null)
statements.forEach(st => {
console.log(`${st.predicate.uri}: ${st.object.value}`)
})

// Get statements where alice is the object
const whoKnowsAlice = store.match(null, FOAF('knows'), alice, null)

Removing Data

// Remove a specific statement
store.remove(st(alice, FOAF('name'), lit('Alice'), doc))

// Remove all statements matching a pattern
store.removeMatches(alice, FOAF('knows'), null, null)

Fetcher

The Fetcher loads RDF data from URIs into the store:

import { graph, Fetcher } from 'rdflib'

const store = graph()
const fetcher = new Fetcher(store)

// Load a resource
await fetcher.load('https://alice.example/profile/card')

// Now query the loaded data
const name = store.any(
sym('https://alice.example/profile/card#me'),
FOAF('name'),
null,
null
)

// Load multiple resources
await fetcher.load([
'https://alice.example/profile/card',
'https://bob.example/profile/card'
])

// Check if a resource is loaded
if (fetcher.requested['https://alice.example/profile/card']) {
console.log('Already loaded')
}

// Force reload
await fetcher.load('https://alice.example/profile/card', { force: true })

Fetcher Options

const fetcher = new Fetcher(store, {
timeout: 30000, // Request timeout in ms
withCredentials: true, // Include cookies for CORS
fetch: customFetch, // Custom fetch implementation
})

UpdateManager

The UpdateManager writes changes back to Solid pods:

import { graph, Fetcher, UpdateManager, sym, lit, st } from 'rdflib'

const store = graph()
const fetcher = new Fetcher(store)
const updater = new UpdateManager(store)

// Load the document first
const doc = sym('https://alice.example/profile/card')
await fetcher.load(doc)

const alice = sym('https://alice.example/profile/card#me')

// Update: delete old statements, insert new ones
const oldName = store.any(alice, FOAF('name'), null, doc)
const newName = lit('Alice Smith')

await updater.update(
oldName ? [st(alice, FOAF('name'), oldName, doc)] : [], // deletions
[st(alice, FOAF('name'), newName, doc)] // insertions
)

// The store is automatically updated after successful PATCH

Creating New Resources

// PUT a new resource
const newDoc = sym('https://alice.example/notes/note1.ttl')
const note = sym('https://alice.example/notes/note1.ttl#it')

store.add(note, RDF('type'), sym('http://example.org/Note'), newDoc)
store.add(note, RDFS('label'), lit('My First Note'), newDoc)

await updater.put(
newDoc,
store.statementsMatching(null, null, null, newDoc),
'text/turtle'
)

Serialization

Parse RDF

import { graph, parse } from 'rdflib'

const store = graph()
const turtle = `
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
<#me> a foaf:Person ;
foaf:name "Alice" .
`

parse(turtle, store, 'https://alice.example/profile/card', 'text/turtle')

Serialize RDF

import { serialize } from 'rdflib'

// Serialize to Turtle
const turtle = serialize(doc, store, null, 'text/turtle')

// Serialize to JSON-LD
const jsonld = serialize(doc, store, null, 'application/ld+json')

// Serialize to N-Triples
const ntriples = serialize(doc, store, null, 'application/n-triples')

// Serialize to RDF/XML
const rdfxml = serialize(doc, store, null, 'application/rdf+xml')

Common Patterns

async function getProfile(webId) {
await fetcher.load(webId)

const person = sym(webId)
const name = store.any(person, FOAF('name'), null, null)
const friends = store.each(person, FOAF('knows'), null, null)

// Load friend profiles
for (const friend of friends) {
await fetcher.load(friend.doc()) // Load the document containing the friend
}

return { name: name?.value, friends }
}

Type Checking

function isContainer(uri) {
return store.holds(sym(uri), RDF('type'), LDP('Container'), null) ||
store.holds(sym(uri), RDF('type'), LDP('BasicContainer'), null)
}

function isPerson(uri) {
return store.holds(sym(uri), RDF('type'), FOAF('Person'), null) ||
store.holds(sym(uri), RDF('type'), VCARD('Individual'), null)
}

Getting Document from URI

// For a URI like https://example.org/doc#thing
// .doc() returns https://example.org/doc

const thing = sym('https://example.org/doc#thing')
const document = thing.doc() // NamedNode for https://example.org/doc

// Load the document containing a resource
await fetcher.load(thing.doc())

TypeScript Types

import type {
NamedNode,
Literal,
BlankNode,
Variable,
Statement,
IndexedFormula,
Fetcher,
UpdateManager,
Node
} from 'rdflib'

// Node is the union type for all node types
type RDFNode = NamedNode | Literal | BlankNode | Variable

// Statement represents a quad
interface Statement {
subject: NamedNode | BlankNode
predicate: NamedNode
object: Node
graph: NamedNode
}

See Also