Kopflos is a headless, RDF-rich CMS that allows you to build data-centric applications using Semantic Web technologies. This tutorial will guide you through the process of building a simple API and frontend, based on the example project.
Before starting, ensure you have the following installed:
- Node.js (latest LTS)
- Docker (optional, if you want to use a persistent database like Oxigraph or Virtuoso)
Create a new directory for your project and initialize it:
mkdir my-kopflos-app
cd my-kopflos-app
npm init -y
npm install @kopflos-cms/core @kopflos-cms/in-memory @kopflos-cms/plugin-deploy-resources @kopflos-cms/express @kopflos-cms/vite @kopflos-cms/hydra @kopflos-cms/shacl @kopflos-labs/pagesThe heart of a Kopflos application is the configuration file. It defines your data sources, plugins, and environment.
Create a kopflos.config.ts file in your project root:
import { KopflosConfig } from '@kopflos-cms/core'
import { createInMemoryClients } from '@kopflos-cms/in-memory'
import DeployResources from '@kopflos-cms/plugin-deploy-resources'
import Hydra from '@kopflos-cms/hydra'
import PluginPages from '@kopflos-labs/pages'
const baseIri = process.env.API_BASE || 'http://localhost:1429'
export default <KopflosConfig>{
baseIri,
sparql: {
// We'll use an in-memory database for this tutorial
default: createInMemoryClients(),
},
plugins: [
// Automatically deploy RDF resources from a directory to the database
new DeployResources({
paths: ['resources'],
}),
// Enable Hydra API documentation
new Hydra(),
// Data-driven page builder
new PluginPages(),
],
}Kopflos uses RDF to define both the data and the API structure.
Create a directory resources and add a sample data file plaques.ttl:
@prefix schema: <http://schema.org/> .
<http://localhost:1429/plaque/newton-apple>
a <http://localhost:1429/api/schema/Plaque> ;
schema:name "Newton's Apple Tree" ;
schema:text "A cutting from the original tree..." .You can define your API structure using Turtle files. Create resources/api/index.ttl:
@prefix kl: <https://kopflos.described.at/> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
<>
a kl:Api ;
kl:resourceLoader kl:OwnGraphLoader .
<#plaque>
a kl:ResourceShape ;
kl:api <> ;
sh:targetClass <http://localhost:1429/api/schema/Plaque> .Sometimes you need custom server-side logic. Kopflos allows you to attach "Handlers" to your resources.
- Define the handler in RDF (update
resources/api/index.ttl):
@prefix code: <https://code.described.at/> .
<#plaque>
kl:handler [
a kl:Handler ;
kl:method "POST" ;
code:implementedBy [
a code:EcmaScriptModule ;
code:link <file:lib/plaque.js#post> ;
]
] .- Implement the handler in TypeScript (
lib/plaque.ts):
import { SubjectHandler } from '@kopflos-cms/core'
export const post = (): SubjectHandler => async (req) => {
// Store the incoming RDF stream into the default SPARQL store
await req.env.sparql.default.stream.store.post(req.body.quadStream, {
graph: req.subject.term,
})
return { status: 204 }
}The @kopflos-labs/pages plugin lets you build pages that are automatically mapped to your RDF resources.
Create a file pages/plaque/[id].ts:
import { html, definePage } from '@kopflos-labs/pages'
export default definePage({
mainEntity: '/plaque/[id]',
// A simple SPARQL query to fetch the plaque's data
queries: {
plaque: (params) => `
PREFIX schema: <http://schema.org/>
CONSTRUCT { ?s ?p ?o }
WHERE {
BIND(<http://localhost:1429/plaque/${params.id}> as ?s)
?s ?p ?o .
}`
},
// Define how the page looks using Lit-style templates
body({ env }) {
return html`
<rdf-environment>
<data-graph data-graph="plaque">
<target-node target-class="http://localhost:1429/api/schema/Plaque">
<header>
<traverse-graph property-path="schema:name">
<resource-label></resource-label>
</traverse-graph>
</header>
<main>
<resource-label property="schema:text"></resource-label>
</main>
</target-node>
</data-graph>
</rdf-environment>`
},
})Add the following scripts to your package.json:
"scripts": {
"start": "kopflos serve --mode development"
}Run the server:
npm startYour API and frontend should now be running at http://localhost:1429.
- API Documentation:
http://localhost:1429/api - Newton's Apple Page:
http://localhost:1429/plaque/newton-apple
In this tutorial, you've learned how to:
- Configure Kopflos with an in-memory database.
- Deploy RDF data and define an API using Turtle.
- Add custom server-side logic with Handlers.
- Create data-driven frontend pages with
@kopflos-labs/pages.