Datenschnittstelle
Über /data/<ns>/<api> wird auf die Datensätze der API api im Namensraum ns zugegriffen. Es gilt ein einfacher CRUD-Ansatz:
| Methode | Wirkung |
|---|---|
POST /data/<ns>/<api> | Legt einen Datensatz an. Er erhält eine vom Server vergebene Kennung (_id) und Meta-Daten (anlegendes Konto, Zeitpunkt). |
PUT /data/<ns>/<api>[/<id>] | Ändert einen Datensatz und aktualisiert die Meta-Daten (letztes Konto, Zeitpunkt). Übertragen werden nur die zu ändernden Felder. Substrukturen müssen immer vollständig übertragen werden. |
DELETE /data/<ns>/<api>/<id> | Löscht einen Datensatz. |
GET /data/<ns>/<api>/<id> | Liefert einen Datensatz. |
GET /data/<ns>/<api> | Liefert die erste Seite (100 Datensätze) aller Datensätze, im Format einer Abfrage. |
POST /data/<ns>/<api>/query | Erweiterte Suche mit Filter, Sortierung und Blättern, siehe Abfragen. |
POST, PUT und DELETE antworten mit dem betroffenen Datensatz.
Bei PUT gibt es drei Möglichkeiten, die Kennung anzugeben:
- nur in der URL (
/data/<ns>/<api>/<id>), im Datensatz fehlt sie, - nur im Datensatz (
_id), die URL ist/data/<ns>/<api>, - auf beiden Wegen, dann müssen beide identisch sein, sonst wird ein Fehler gemeldet.
Fehler meldet das CMS mit Status 400 und { "error": "<Meldung>" }, auch bei unbekannten Datensätzen. Fehlende Rechte meldet es mit 403.
Aufbau eines Datensatzes
{
"_id": "894758ed-e701-4839-a085-3b77bb864d52",
"_cms": {
"created": "2026-10-21T09:12:44.123Z",
"createdBy": "<Kennung des Kontos>",
"lastModified": "2026-10-21T10:01:02.456Z",
"modifiedBy": "<Kennung des Kontos>"
},
"name": "Nicolas"
}
_cms wird vom CMS gepflegt und kann nicht gesetzt werden. _cms.lastModified eignet sich, um nur geänderte Datensätze abzurufen (siehe Abfragen).
Beispiele
const cms = 'https://cms.example.org'
const headers = {
Authorization: 'Basic ' + btoa('CMS_User_01:<kennwort>'),
'Content-Type': 'application/json',
}
const people = `${cms}/data/tutorial/people`
async function call(url, options) {
const res = await fetch(url, { headers, ...options })
if (!res.ok) throw new Error(`${res.status}: ${await res.text()}`)
return res.json()
}
// Anlegen
const created = await call(people, { method: 'POST', body: JSON.stringify({ name: 'Nicolas', email: 'nicolas@example.org' }) })
// Einzeln lesen
const one = await call(`${people}/${created._id}`)
// Ändern: nur die geänderten Felder
await call(`${people}/${created._id}`, { method: 'PUT', body: JSON.stringify({ age: 43 }) })
// Suchen
const { items, filterCount } = await call(`${people}/query`, {
method: 'POST',
body: JSON.stringify({ filter: { age: { $gte: 18 } }, sort: { name: 'ascending' }, pageSize: 20 }),
})
// Löschen
await call(`${people}/${created._id}`, { method: 'DELETE' })
Dasselbe mit curl:
CMS=https://cms.example.org
USER='CMS_User_01:<kennwort>'
curl -X POST -u "$USER" -H 'Content-Type: application/json' -d '{ "name": "Nicolas", "email": "nicolas@example.org" }' "$CMS/data/tutorial/people"
curl -u "$USER" "$CMS/data/tutorial/people/<id>"
curl -X PUT -u "$USER" -H 'Content-Type: application/json' -d '{ "age": 43 }' "$CMS/data/tutorial/people/<id>"
curl -X POST -u "$USER" -H 'Content-Type: application/json' -d '{ "filter": { "age": { "$gte": 18 } } }' "$CMS/data/tutorial/people/query"
curl -X DELETE -u "$USER" "$CMS/data/tutorial/people/<id>"
Abruf für die Darstellung (?exported)
Mit dem Parameter ?exported (bei GET und POST query) liefert das CMS die Datensätze in einer Form, die direkt angezeigt werden kann:
- HTML-Felder enthalten fertiges HTML statt des internen draft-js-Formats.
- In Dateifeldern (und ihren Vorschaubildern) enthält
pathdie vollständige Abruf-URL (ohne Rechner und Port).
curl -u "$USER" "$CMS/data/tutorial/file/<id>?exported"
So abgerufene Datensätze sind nicht zum Zurückschreiben gedacht.
Dateien
Abrufen
Zum Abruf einer Datei braucht das Konto Leserecht auf die API, in deren Datensatz die Datei steht. Normal abgerufen sieht ein Dateifeld so aus:
{
"path": "ABCD",
"name": "myself.png",
"mime": "image/png",
"size": 1289173
}
path ist eine vom CMS vergebene, über alle Namensräume und APIs eindeutige Kennung. Der Inhalt liegt unter /media/<ns>/<api>/<path>, also analog zur Datenschnittstelle, nur mit media statt data und mit der Kennung der Datei statt der des Datensatzes: /media/tutorial/file/ABCD.
Mit ?exported steht die URL bereits im Feld:
{
"path": "/media/tutorial/file/ABCD",
"name": "myself.png",
"mime": "image/png",
"size": 1289173
}
/media unterstützt Teilabrufe (HTTP Range), wichtig für das Abspielen von Videos. Für die direkte Einbindung in <img> oder <video> hilft ein Kurzzeit-Token als Parameter ?token=….
Einspielen
Datensätze mit Dateien werden als multipart/form-data übertragen. Das JSON des Datensatzes steht als Zeichenkette (JSON.stringify) im Formularwert data. Jedes Dateifeld enthält mindestens:
{
"path": "12345",
"name": "myself.png"
}
path ist hier der Name eines weiteren Formularwerts, der den Dateiinhalt enthält.
curl -X POST -u "$USER" \
-F 'data={ "title": "Ein erstes Bild", "image": { "path": "bild1", "name": "myself.png" } }' \
-F 'bild1=@myself.png' \
"$CMS/data/tutorial/file"
const form = new FormData()
form.append('data', JSON.stringify({ title: 'Ein erstes Bild', image: { path: 'bild1', name: 'myself.png' } }))
form.append('bild1', file) // z. B. aus einem <input type="file">
await fetch(`${cms}/data/tutorial/file`, {
method: 'POST',
headers: { Authorization: headers.Authorization }, // Content-Type setzt der Browser selbst
body: form,
})
Das gilt für POST und PUT gleichermaßen.