Get started with RDF in JavaScript

In case you have little to no experience with RDF you might want to read the RDF Primer first, which gives a good basic introduction to the concepts of RDF. Further, you should know how to use NPM packages in your JavaScript projects.

Load RDF data in your application

One of the first things you want to do is to load existing RDF data and do some work on it in JavaScript. Basically, we need an RDF equivalent of JSON.parse(), but instead of JSON we load RDF from an existing resource. We provide abstractions that allow you to load data from different sources in a similar fashion.

Load data from a file

npm i rdf-utils-fs

This example loads a local RDF file and parses the data into a dataset. It can parse common RDF formats automatically.

require('tbbt-ld')
const { join, dirname } = require('path')
//           node_modules/tbbt-ld/dist/tbbt.nq
const tbbt = join(dirname(require.resolve('tbbt-ld')), 'dist/tbbt.nq')

const fromFile = require('rdf-utils-fs/fromFile')
const stream = fromFile(tbbt)

stream.on('data', quad => {
  console.log(`${quad.subject.value} ${quad.predicate.value} ${quad.object.value}`)
})

When you load data from a file, you will always get back a stream.

Load data from HTTP

npm i @rdfjs/fetch

This example loads an RDF file available on an HTTP resource and parses the data into a dataset. It can parse common RDF formats automatically.

const fetch = require('@rdfjs/fetch')

const res = await fetch('https://zazuko.github.io/tbbt-ld/dist/tbbt.nq')
const dataset = await res.dataset()

for (const quad of dataset) {
  console.log(`${quad.subject.value} ${quad.predicate.value} ${quad.object.value}`)
}

Load data from SPARQL endpoint

npm i sparql-http-client

This example is using the SPARQL 1.1 Query Language, in particular a CONSTRUCT query that returns an RDF graph.

const SparqlClient = require('sparql-http-client')

const client = new SparqlClient({ endpointUrl: 'http://dbpedia.org/sparql' })
const stream = await client.query.construct('DESCRIBE <http://dbpedia.org/resource/RDF>')

stream.on('data', quad => {
  console.log(`${quad.subject.value} ${quad.predicate.value} ${quad.object.value}`)
})

When you load data from a SPARQL endpoint, you will always get back a stream.

Load into a dataset structure

npm i sparql-http-client

In most cases you will want to load the data from the stream into a dataset. As you saw above, this is only directly possible by using the fetch function, all other functions only provide a stream interface. To load any of these streams into a dataset, you can use the following code snippet:

const rdf = require('rdf-ext')
const SparqlClient = require('sparql-http-client')

const client = new SparqlClient({ endpointUrl: 'http://dbpedia.org/sparql' })
const stream = await client.query.construct('DESCRIBE <http://dbpedia.org/resource/RDF>')

const dataset = rdf.dataset()
await dataset.import(stream)

for (const quad of dataset) {
  console.log(`${quad.subject.value} ${quad.predicate.value} ${quad.object.value}`)
}

Interact on RDF Data in your application

We obviously want to do something with the data. For that we provide several libraries that facilitate interacting with RDF data in JavaScript.

Understand namespaces

npm i @rdfjs/namespace

RDF is heavily using the concept of IRIs as identifiers for things. This is very powerful and heavily used in more complex RDF based applications. However, it is not necessarily nice to work with these IRIs in code so we provide some syntactic sugar to improve readability of the code when working with IRIs. For that you can use the following package:

const namespace = require('@rdfjs/namespace')

const ns = {
  schema: namespace('http://schema.org/'),
  rdf: namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#')
}

// create given name named node
const givenName = ns.schema.givenName
const type = ns.rdf.type

console.log(`${givenName.value} (${givenName.termType})`)
console.log(`${type.value} (${type.termType})`)

Traverse an RDF graph

There are many ways to interact with triples once they are available in a dataset. There are two basic specifications that define a common interface implemented and used by multiple libraries in the JavaScript RDF world:

We do not recommend to start with them right away, because most of the time you do not need the kind of low-level interfaces defined by those specifications. Most of our libraries either implement these specifications or use them as a foundation.

For most interactions with RDF, you want to use an abstraction that is more graph-oriented and provides helper methods for common interactions on a graph. For that we built the clownface library:

npm i clownface

const cf = require('clownface')
const fetch = require('@rdfjs/fetch')
const namespace = require('@rdfjs/namespace')

const ns = {
  schema: namespace('http://schema.org/'),
  rdf: namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#')
}

const dataset = await fetch('http://zazuko.github.io/tbbt-ld/dist/tbbt.nt')
  .then(response => response.dataset())

const tbbt = cf({ dataset })
const stuartBloom = tbbt.namedNode('http://localhost:8080/data/person/stuart-bloom')

const peopleStuartKnows = stuartBloom
  .out(ns.schema.knows)
  .map((person) => {
    const personalInformation = person.out([
      ns.schema.givenName,
      ns.schema.familyName
    ])
    return personalInformation.values.join(' ')
  })
  .join(', ')

console.log(peopleStuartKnows)

Create a new graph with the clownface library

We also manipulate graphs with clownface and add triples to it:

const cf = require('clownface')
const rdf = require('rdf-ext')
const namespace = require('@rdfjs/namespace')

const ns = {
  schema: namespace('http://schema.org/'),
  rdf: namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#')
}

const zazuko = cf({ dataset: rdf.dataset() })

zazuko
  .namedNode('http://example.org/zazuko')
  .addOut(ns.rdf.type, ns.schema.Organisation)
  .addOut(ns.rdf.name, 'Zazuko')

for (const quad of zazuko.dataset) {
  console.log(`${quad.subject.value} ${quad.predicate.value} ${quad.object.value}`)
}

Persist RDF to disk or store

npm i rdf-utils-fs

After you create or update data in RDF, you want to store it in some way. In this example we serialize triples into a file on the filesystem:

const { readFileSync } = require('fs')
const rdf = require('rdf-ext')
const toFile = require('rdf-utils-fs/toFile')
const namespace = require('@rdfjs/namespace')

const ns = {
  ex: namespace('http://example.org/'),
  schema: namespace('http://schema.org/'),
  rdf: namespace('http://www.w3.org/1999/02/22-rdf-syntax-ns#')
}

const dataset = rdf.dataset()

dataset.addAll([
  rdf.quad(ns.ex.zazuko, ns.rdf.type, ns.schema.Organisation),
  rdf.quad(ns.ex.zazuko, ns.rdf.name, rdf.literal('Zazuko'))
])

await toFile(dataset.toStream(), 'test.ttl')

console.log(readFileSync('test.ttl').toString())

Analytics on RDF data

Run SELECT SPARQL Queries to create Tables

npm i sparql-http-client

Whether you are consuming RDF data in your intranet or in the Linked Open Data Cloud, you will often interact with the SPARQL protocol family.

For any kind of interaction with SPARQL we provide a powerful library called sparql-http-client. This library provides a JavaScript based abstraction for easier handling of SPARQL Queries and Graph Store requests.

We use it to fetch the height of the Eiffel Tower from DBpedia:

const SparqlClient = require('sparql-http-client')

const client = new SparqlClient({ endpointUrl: 'http://dbpedia.org/sparql' })
const stream = await client.query.select(`
  PREFIX dbr: <http://dbpedia.org/resource/>
  PREFIX dbp: <http://dbpedia.org/property/>

  SELECT * WHERE {
    dbr:Eiffel_Tower dbp:height ?height .
  }
`)

stream.on('data', row => {
  console.log(row.height.value)
})

Fetch data through a SELECT SPARQL query to hand over to D3

npm i d3-sparql

For visualizing data, D3.js is a popular and powerful library. With d3-sparql we provide a library that makes it easy to wrap data from SPARQL into the commonly used JavaScript structures of d3-csv.

In this example we cannot do a visualization due to RunKit limitations but we can print the highest building of the world to the console:

const d3  = require('d3-sparql')
const fetch = require('isomorphic-fetch')

var wikidataUrl = 'https://query.wikidata.org/bigdata/namespace/wdq/sparql'

var skyScrapersQuery = `
  SELECT ?item ?itemLabel (MAX(?heights) as ?height)  
  WHERE 
  {
    ?item wdt:P31 wd:Q11303.
    FILTER NOT EXISTS { ?item wdt:P31 wd:Q1570262}

    ?item p:P2048/psn:P2048/wikibase:quantityAmount ?heights.
    ?item rdfs:label ?itemLabel.
    FILTER(LANG(?itemLabel) = 'en')

  }
  GROUP BY ?item ?itemLabel
  ORDER BY DESC(?height)
`

d3.sparql(wikidataUrl, skyScrapersQuery).then(function(data) {
  console.log(data)
})

Futher documentation, support & questions

All our libraries are released under the MIT license.

Zazuko provides commercial support for these libraries, please get in contact with us if you would like to know more.

  • If you have questions we recommend to start a topic on the rdf.community discussion forum, for example in the RDF Tooling / Libraries category category.
  • If you find bugs feel free to open an issue in the appropriate GitHub repository.
  • In case you miss some functionality or documentation is unclear, please discuss that in the discussion forum first. If it leads to new ideas we can create GitHub issues for it.
  • And last but not least, pull requests are more than welcome!

This document provides a brief introduction, to dive deeper into the topics please have a look at:

  • rdf-ext - The entry page for our RDF Interfaces Extensions provides an overview of all libraries that are currently available. This is a great starting point if you want to dive deeper and do more low-level things with RDF in JavaScript. This page links to work from many other groups and people, like the RDFJS effort.
  • Within rdf-ext we have a documentation repository that provides more information about how to work with low-level interfaces.