Skip to main content

RDF Primer

Resource Description Framework (RDF) is the data model underlying Solid. Understanding RDF is essential for building SolidOS panes.

Core Concepts

Triples

RDF data consists of triples: subject-predicate-object statements.

<subject> <predicate> <object> .

Example:

<https://alice.example/profile/card#me> <http://xmlns.com/foaf/0.1/name> "Alice Smith" .

This says: "Alice's profile has the name 'Alice Smith'"

URIs

Resources are identified by URIs (Uniform Resource Identifiers):

<https://alice.example/profile/card#me>   # A person
<http://xmlns.com/foaf/0.1/name> # The "name" property
<https://alice.example/photo.jpg> # An image

Literals

Values like strings, numbers, and dates:

"Alice Smith"                              # Plain string
"Alice Smith"@en # String with language tag
"42"^^<http://www.w3.org/2001/XMLSchema#integer> # Typed literal

Blank Nodes

Anonymous nodes without URIs:

<#me> foaf:knows [
foaf:name "Bob" ;
foaf:mbox <mailto:bob@example.org>
] .

Turtle Syntax

Turtle is the most common RDF serialization in Solid.

Prefixes

@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix solid: <http://www.w3.org/ns/solid/terms#> .

<#me> a foaf:Person ;
foaf:name "Alice" .

Multiple Properties

Use semicolon to add properties to same subject:

<#me>
foaf:name "Alice" ;
foaf:mbox <mailto:alice@example.org> ;
foaf:knows <https://bob.example/profile/card#me> .

Multiple Values

Use comma for multiple values of same property:

<#me> foaf:knows
<https://bob.example/profile/card#me>,
<https://carol.example/profile/card#me>,
<https://dave.example/profile/card#me> .

Common Vocabularies

FOAF (Friend of a Friend)

@prefix foaf: <http://xmlns.com/foaf/0.1/> .

<#me> a foaf:Person ;
foaf:name "Alice" ;
foaf:nick "alice" ;
foaf:mbox <mailto:alice@example.org> ;
foaf:img <photo.jpg> ;
foaf:knows <https://bob.example/profile/card#me> .

vCard

@prefix vcard: <http://www.w3.org/2006/vcard/ns#> .

<#me>
vcard:fn "Alice Smith" ;
vcard:hasEmail <mailto:alice@example.org> ;
vcard:hasTelephone <tel:+1-555-1234> ;
vcard:hasAddress [
vcard:street-address "123 Main St" ;
vcard:locality "San Francisco" ;
vcard:region "CA"
] .

Solid Terms

@prefix solid: <http://www.w3.org/ns/solid/terms#> .
@prefix space: <http://www.w3.org/ns/pim/space#> .
@prefix ldp: <http://www.w3.org/ns/ldp#> .

<#me>
solid:oidcIssuer <https://solidcommunity.net> ;
solid:publicTypeIndex </settings/publicTypeIndex.ttl> ;
solid:privateTypeIndex </settings/privateTypeIndex.ttl> ;
space:preferencesFile </settings/prefs.ttl> ;
space:storage </> ;
ldp:inbox </inbox/> .

Schema.org

@prefix schema: <http://schema.org/> .

<#event> a schema:Event ;
schema:name "Team Meeting" ;
schema:startDate "2024-01-15T10:00:00Z"^^xsd:dateTime ;
schema:location "Conference Room A" .

Working with RDF in JavaScript

Using rdflib.js

import { graph, sym, lit, Namespace, parse } from 'rdflib'

// Create a store
const store = graph()

// Define namespaces
const FOAF = Namespace('http://xmlns.com/foaf/0.1/')
const RDF = Namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#')

// Create nodes
const alice = sym('https://alice.example/profile/card#me')
const name = lit('Alice Smith')

// Add triple
store.add(alice, FOAF('name'), name, alice.doc())

// Query
const aliceName = store.any(alice, FOAF('name'))
console.log(aliceName.value) // "Alice Smith"

Parsing Turtle

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

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

Querying

// Single value
const name = store.any(subject, FOAF('name'), null, doc)

// All values
const friends = store.each(subject, FOAF('knows'), null, doc)

// Check existence
const isPerson = store.holds(subject, RDF('type'), FOAF('Person'))

// Match patterns
const statements = store.match(subject, null, null, doc)

Named Graphs

RDF statements can be organized into named graphs:

// Fourth element is the graph (document)
store.add(alice, FOAF('name'), lit('Alice'), alice.doc())

// Query within a specific graph
const name = store.any(alice, FOAF('name'), null, alice.doc())

Document vs Fragment

In Solid, a document can contain multiple subjects:

https://alice.example/profile/card      # The document
https://alice.example/profile/card#me # Alice (fragment)
https://alice.example/profile/card#key # Her public key (fragment)
const doc = sym('https://alice.example/profile/card')
const alice = sym('https://alice.example/profile/card#me')

// Load the document
await store.fetcher.load(doc)

// Query Alice's data
const name = store.any(alice, FOAF('name'), null, doc)

See Also