Skip to the content.

Open Data App Spezifikation

Eine Open Data App (ODA) ist eine statische Web-App, die offene Daten aus einem Open Data Portal lädt, visualisiert oder anderweitig nutzbar macht. ODAs können im Open Data App Store (ODAS) veröffentlicht und dort von Portalbetreibern für ihr eigenes Portal konfiguriert werden.

Diese Spezifikation trennt drei Ebenen:

ODAS-Kompatibilität

Eine ODA muss:

Eine ODA darf zusätzliche JavaScript- oder CSS-Bibliotheken verwenden, solange diese für die App-Funktion notwendig oder nachvollziehbar sind und keine Sicherheitsrisiken erzeugen.

Empfohlene Architektur

Für neue Apps wird die Vorlage oda-generic empfohlen. Sie definiert den üblichen Aufbau:

oda-example/
├── app/
│   ├── index.html
│   ├── app-base.js
│   ├── app-base.css
│   ├── app.js
│   ├── app.css
│   ├── favicon.png
│   └── logo_ondics.png
├── assets/
│   ├── odas-app-icon.svg
│   ├── Desktop_Screenshot.png
│   ├── Mobile_Screenshot.png
│   ├── branding.css
│   └── schema.json
├── odas-config/
│   └── config.json
├── app-package.json
├── CHANGELOG.md
├── README.md
├── Dockerfile
├── docker-compose.yml
├── nginx.conf
└── Makefile

Bei Verwendung von oda-generic gilt als empfohlene Konvention:

Andere statische Implementierungen sind möglich, solange sie die ODAS-Kompatibilität erfüllen.

app-package.json

Die Datei app-package.json enthält Metadaten, Datenbeschreibung und die Konfigurationsfelder, aus denen ODAS die Instanz-Konfiguration erzeugt.

Wichtige Top-Level-Felder sind:

Apps mit api-version: "1" werden in dieser Doku als ODAS-v1-Apps bezeichnet; das ist die aktuelle Version der ODAS-Config-API.

instanz-config

instanz-config beschreibt die Formularfelder, die ein Portalbetreiber im ODAS pro App-Instanz ausfüllt. Jeder Key muss eindeutig sein und sollte genau den Namen verwenden, den die App im configdata liest.

Ein Feld enthält typischerweise:

Robust unterstützte und empfohlene format.typ-Werte für ODAS-v1 sind:

Zahlen sollten in instanz-config als Strings gespeichert und in der App bei Bedarf mit Number(...), parseInt(...) oder ähnlichen Funktionen umgewandelt werden. Das vermeidet Probleme in Import- und Editorpfaden.

Die Typnamen werden exakt kleingeschrieben verwendet: string, nicht String; Typen wie Zahl oder number gehören nicht zu den oben genannten, für ODAS-v1 robusten Typen. Auch die Key-Namen sind kanonisch: Der Datenendpunkt heißt apiurl (komplett klein), die Datensatzseite urlDaten. Parallele Legacy-Schreibweisen wie apiUrl oder doppelte urldaten/urlDaten-Definitionen sollen nicht neu eingeführt werden.

dropdown-Felder schreiben ihre Vorbelegung als Feld-default; format enthält den Typ und die Optionen:

{
  "default": "nein",
  "format": {
    "typ": "dropdown",
    "optionen": ["nein", "ja"]
  }
}

Multiline und Rich Text

Längere Werte können als String-Array mit dem Marker _multiline_ gespeichert werden: Jeder Wert in der app-package.json, der ein Array von Strings ist und dessen erster String "_multiline_" lautet, wird als Multiline-String interpretiert. Der Marker wird entfernt und die übrigen Zeilen werden verbunden.

Beispiel für einen Wert, der als Multiline-String definiert ist:

{
  "mehrzeiliger-string": [
    "_multiline_",
    "erste Zeile ",
    "zweite Zeile\n",
    "dritte Zeile"
  ]
}

Dieser Wert wird im ODAS interpretiert als:

{
  "mehrzeiliger-string": "erste Zeile zweite Zeile\ndritte Zeile"
}

format.typ: "markdown" ist der ODAS-v1-Feldtyp für Rich Text. Felder dieses Typs werden in Markdown verfasst: Überschriften mit ##, Listen mit -, Links mit [Text](URL). ODAS wandelt das Markdown vor der Auslieferung der Konfiguration an die App in HTML um; die App rendert das Ergebnis unverändert (z.B. per innerHTML). Portalbetreiber können Felder wie Impressum, Kontakt oder Beschreibung dadurch ohne HTML-Kenntnisse pflegen.

Hinweis zur Übergangsphase: Bestehende Apps und Konfigurationen enthalten in Markdown-Feldern teilweise direkt HTML (z.B. <h2>, <p>, <ul>). Das bleibt während der Übergangsphase gültig; für neue und überarbeitete Inhalte ist Markdown der Standard. Das Beispiel-app-package.json weiter unten zeigt den aktuellen Stand der Vorlage oda-generic und enthält deshalb noch HTML-Defaults.

Statische Beschreibungstexte bleiben im bestehenden Feld beschreibung; dafür werden keine zusätzlichen Config-Keys angelegt. Die Datenquellenbeschreibung verlinkt, soweit Werte vorhanden sind, vom Open Data Portal über die Datensatzseite bis zur tatsächlich verwendeten Ressource oder API.

Konfiguration

Zur Laufzeit erhält die App ein configdata-Objekt aus dem ODAS. Bei oda-generic werden unter anderem diese Basisfelder verwendet:

Empfohlen ist: Jeder app-spezifische Key, den app/app.js liest, ist auch in app-package.json unter instanz-config dokumentiert. Für lokale Tests spiegelt odas-config/config.json diese Werte. odas-config/config.json ist dabei eine lokale Entwicklungsdatei; im ODAS wird die produktive Konfiguration aus den App-Metadaten und der jeweiligen Instanz erzeugt. Beim Anlegen einer App-Instanz übernimmt der ODAS zunächst die default-Werte und die in der app-package.json eingetragenen Werte; der Portalbetreiber passt sie anschließend über das Konfigurationsformular an.

Daten und Assets

Der Abschnitt daten in app-package.json beschreibt die erwarteten Daten:

Assets liegen üblicherweise unter assets/. Screenshotpfade werden relativ und ohne führenden Slash angegeben. Das Store- und Runtime-Icon zeigen im Normalfall beide auf assets/odas-app-icon.svg. Alle referenzierten Dateien müssen im aktiven ZIP-Lieferumfang vorhanden sein.

Der Dateiname _odp-logo.png ist in assets/ nicht zulässig; er ist für den ODAS reserviert.

Neue Apps liefern ein echtes Frictionless Table Schema mit einem flachen fields-Array. Ein leeres Objekt, Nutzdaten anstelle eines Schemas oder ein allgemeines JSON Schema erfüllen diese Konvention nicht.

ODA-Styling

ODAs sollen gleiche CSS-Grundlagen verwenden, damit sie sich in jedes Open Data Portal einheitlich einfügen. Die Basis bilden Bootstrap 5.3.8 und die Vorlagen-Datei app/app-base.css aus oda-generic; app-spezifisches Styling kommt zusätzlich in app/app.css.

Portalbetreiber können das Corporate Design pro App-Instanz anpassen, sodass die App nach dem Betreiber, der Stadt oder dem Open Data Portal aussieht. Dafür gibt es zwei Laufzeit-Konfigurationsfelder:

Die Vorlage liefert außerdem assets/branding.css als mitgelieferte Branding-Basisdatei mit; das Top-Level-Feld branding-css in app-package.json kann auf eine solche Datei zeigen.

CORS und ODAS-Proxy

Viele offene Datenquellen lassen sich direkt im Browser laden. Wenn eine Quelle CORS blockiert oder aus Sicherheitsgründen nicht direkt angesprochen werden soll, kann der ODAS-Proxy verwendet werden.

Für proxy-fähige Apps wird der Konfigurationsschalter proxyAktiv empfohlen:

"proxyAktiv": {
  "label": "ODAS-Proxy aktivieren",
  "hilfe": "Mit ja werden Datenabrufe über den ODAS-Proxy gesendet. Echte Proxy-Aufrufe sind nur im ODAS-Live-System prüfbar.",
  "default": "nein",
  "format": {
    "typ": "dropdown",
    "optionen": ["nein", "ja"]
  },
  "erforderlich": "ja"
}

Der Schalter ist keine allgemeine Pflicht. Er ist sinnvoll, wenn Portalbetreiber bewusst zwischen Direktabruf und Proxy-Betrieb wählen sollen.

Der Proxy-Aufruf verwendet POST. Im URL-kodierten Parameter path wird nur der Pfad einschließlich Query der Zielressource übertragen, nicht die vollständige externe URL. Die Proxy-Antwort enthält die Nutzdaten als String im Feld content. Echte Proxy-Antworten werden im ODAS-Live-System getestet; lokal werden Konfiguration, Statusanzeige und Direktmodus geprüft.

Auslieferung

Der Lieferumfang wird durch das jeweilige Makefile bestimmt. In der aktuellen oda-generic-Vorlage packt make zip:

Dateien wie odas-config/, lokale Tests, Tools oder Demo-Daten sind nur dann online verfügbar, wenn das aktive Makefile sie ausdrücklich in die ZIP-Datei aufnimmt.

Die Paketversion muss mit dem obersten versionierten Eintrag in CHANGELOG.md übereinstimmen. Für neue Einträge wird das Format ## X.Y.Z - YYYY-MM-DD verwendet.

Gekürztes Beispiel app-package.json

{
  "app-entwickler-id": "12343",
  "app-entwickler-name": "ondics-gmbh",
  "name-in-url": "generic",
  "name": "Generic Open Data App",
  "version": "1.1.0",
  "odas-app-icon": "assets/odas-app-icon.svg",
  "app-icon": "assets/odas-app-icon.svg",
  "kurzbeschreibung": "Technische ODAS-Vorlage mit Routing, Instanz-Konfiguration und Proxy-Muster.",
  "beschreibung": [
    "_multiline_",
    "Die Generic Open Data App ist die technische Ausgangsbasis fuer neue ODAS-Apps.",
    "Vor einer Veroeffentlichung werden Fachlogik, Metadaten und Datenmodell ersetzt."
  ],
  "tags-funktion": ["Vorlage", "Konfigurationsvorschau", "Proxy"],
  "tags-dateityp": ["JSON"],
  "tags-datenart": ["Konfigurationsdaten"],
  "screenshots": [
    "assets/Desktop_Screenshot.png",
    "assets/Mobile_Screenshot.png"
  ],
  "daten": {
    "beschreibung": "Die Vorlage enthaelt ein gueltiges Frictionless-Beispielschema.",
    "schema": "assets/schema.json",
    "beispiel": "Vor der Veroeffentlichung durch app-spezifische Angaben ersetzen.",
    "beispiel-url": ""
  },
  "branding-css": "",
  "api-version": "1",
  "instanz-config": {
    "seitentitel": {
      "label": "Seitentitel",
      "hilfe": "Der Seitentitel wird im Browser-Tab der App angezeigt.",
      "default": "Generic Open Data App",
      "format": {
        "typ": "string",
        "laenge": 50
      },
      "erforderlich": "ja"
    },
    "titel": {
      "label": "Titel",
      "hilfe": "Der Titel wird in der Titelzeile der App angezeigt.",
      "default": "Generic Open Data App",
      "format": {
        "typ": "string",
        "laenge": 50
      },
      "erforderlich": "ja"
    },
    "icon": {
      "label": "App-Icon",
      "hilfe": "Das Icon wird links oben in der Titelzeile angezeigt.",
      "default": "{{{odp.logo}}}",
      "format": {
        "typ": "image",
        "hoehe": 100,
        "breite": 300
      },
      "erforderlich": "ja"
    },
    "kontakt": {
      "label": "Kontakt",
      "hilfe": "Der Text wird im Menuepunkt Kontakt angezeigt.",
      "format": {
        "typ": "markdown"
      },
      "default": [
        "_multiline_",
        "<p>Bei Fragen zur App wenden Sie sich bitte an die im Open Data Portal hinterlegte Kontaktstelle.</p>"
      ],
      "erforderlich": "ja"
    },
    "beschreibung": {
      "label": "Beschreibung",
      "hilfe": "Der Text wird im Menuepunkt Ueber diese App angezeigt.",
      "format": {
        "typ": "markdown"
      },
      "default": [
        "_multiline_",
        "<h2>Ueber diese Vorlage</h2>",
        "<p>Diese App zeigt die wirksame Instanz-Konfiguration.</p>",
        "<h2>Datenquelle</h2>",
        "<p>Die Generic-App laedt absichtlich keinen Fachdatenbestand.</p>"
      ],
      "erforderlich": "ja"
    },
    "impressum": {
      "label": "Impressum",
      "hilfe": "Der Text wird im Menuepunkt Impressum angezeigt.",
      "format": {
        "typ": "markdown"
      },
      "default": [
        "_multiline_",
        "<h2>Anbieter</h2>",
        "<p>{{{odp.anbieter.orgName}}}<br>{{odp.anbieter.strasse}}<br>{{odp.anbieter.plzort}}</p>"
      ],
      "erforderlich": "ja"
    },
    "datenschutz": {
      "label": "Datenschutz",
      "hilfe": "Der Text wird im Menuepunkt Datenschutz angezeigt.",
      "format": {
        "typ": "markdown"
      },
      "default": [
        "_multiline_",
        "<p>Massgeblich sind die Datenschutzangaben des jeweiligen Portalbetreibers.</p>"
      ],
      "erforderlich": "ja"
    },
    "fusszeile": {
      "label": "Fusszeile",
      "hilfe": "Wird unten in der App angezeigt.",
      "format": {
        "typ": "string",
        "laenge": 180
      },
      "default": "© {{jahr}} | App und Daten: {{odp.anbieter.name}} | Entwicklung: {{app.developer.name}} | ODAS: {{odas.betreiber.name}}",
      "erforderlich": "ja"
    },
    "brandingCSS": {
      "label": "Zusaetzliches Branding-CSS",
      "hilfe": "Optionaler CSS-Code fuer die konkrete Instanz.",
      "default": "",
      "format": {
        "typ": "string",
        "laenge": 10000
      },
      "erforderlich": "nein"
    },
    "brandingCSSFile": {
      "label": "Branding-CSS-Datei",
      "hilfe": "Optionale URL zu einer Branding-CSS-Datei.",
      "default": "",
      "format": {
        "typ": "url",
        "laenge": 500
      },
      "erforderlich": "nein"
    },
    "urlDaten": {
      "label": "URL zum Datensatz",
      "hilfe": "Katalogseite des tatsaechlich verwendeten Datensatzes.",
      "default": "",
      "format": {
        "typ": "url",
        "laenge": 500
      },
      "erforderlich": "nein"
    },
    "apiurl": {
      "label": "URL zu den Daten",
      "hilfe": "Von dieser URL werden die Daten der App bezogen.",
      "format": {
        "typ": "url",
        "laenge": 255
      },
      "default": "",
      "beispiel": "",
      "erforderlich": "nein"
    },
    "proxyAktiv": {
      "label": "ODAS-Proxy aktivieren",
      "hilfe": "Steuert direkte oder proxygestuetzte Datenabrufe.",
      "default": "nein",
      "format": {
        "typ": "dropdown",
        "optionen": ["nein", "ja"]
      },
      "erforderlich": "ja"
    }
  }
}

Schale 4: Verständlichkeitsebene

Schale 4 ist eine optionale Ebene, die Apps für Bürgerinnen und Bürger ohne Datenfachwissen verständlicher macht: Kontext zu Kennzahlen, Methodik-Transparenz, Datenaktualität und weiterführende Links. Sie ist keine ODAS-Pflicht, sondern Teil des Empfohlen-/Ondics-Standards und in nahezu allen Ondics-Apps umgesetzt.

Grundprinzip: Jede konfigurierbare Komponente rendert nur, wenn ihr Feld gefüllt ist; ein leerer Wert blendet die Komponente aus. Alle Felder verwenden die für ODAS-v1 robusten format.typ-Werte und sind mit erforderlich: "nein" deklariert. Die Komponenten sind Opt-in und werden nur aufgenommen, wenn sie für die konkrete App sinnvoll sind.

KPI-Kontexttexte

Für jeden KPI-Slot kann ein Feld kpiKontext1, kpiKontext2, … (Typ string) einen kurzen Erklärtext liefern. Die Referenz-Apps zeigen ihn über ein kleines ⓘ-Icon zum Aufklappen direkt an der Kennzahl, damit KPI-Reihen kompakt bleiben.

Methodikbox

Eine ausklappbare Sektion im Hauptinhalt erklärt Herkunft, Erhebungsmethode und Limitierungen der Daten. Sie wird aus zwei Feldern gespeist: datenquelleHinweis (Typ markdown) für den Erklärtext und datenStand (Typ string) für eine Freitext-Angabe wie “Stand: Januar 2026”. datenStand ist bewusst ein string und kein Datumstyp.

Datenaktualitäts-Indikator

Wenn die Datenquelle einen Zeitstempel liefert (z.B. CKAN metadata_modified oder der neueste Datensatz), zeigt die App ihn prominent im Inhaltsbereich an. Dafür gibt es bewusst kein Konfigurationsfeld: Der Wert wird quellspezifisch aus der API-Antwort abgeleitet. Liefert die Quelle keinen Zeitstempel, entfällt die Anzeige.

Das Feld weiterfuehrendeLinks (Typ markdown) enthält eine vom Portalbetreiber gepflegte Liste verwandter Datensätze oder Hintergrundquellen. Die App rendert daraus einen Abschnitt “Weitere Informationen” am Ende der Seite.

“Für wen ist diese App?”

Ein kurzer Absatz innerhalb des bestehenden beschreibung-Felds benennt die Zielgruppe und stellt klar, dass kein besonderes Datenfachwissen nötig ist. Dafür wird kein eigener Config-Key angelegt.

Datenquellen-Linkliste

Die Datenquellenbeschreibung in beschreibung verlinkt dreistufig: Open Data Portal → Datensatz → Ressource(n). Die URLs werden aus den Datenquellen-Configwerten der App abgeleitet (urlDaten für die Datensatzseite, apiurl für die Ressource), damit verlinkte und tatsächlich geladene Quelle identisch sind. Ebenen ohne Wert werden weggelassen, nicht mit Platzhaltern verlinkt.

Beispiel

"kpiKontext1": {
  "label": "KPI-Kontext 1",
  "hilfe": "Optionaler Erklärtext zum ersten KPI-Wert. Leer = kein Kontext.",
  "format": { "typ": "string", "laenge": 160 },
  "default": "",
  "erforderlich": "nein"
},
"datenquelleHinweis": {
  "label": "Methodik / Datenquelle-Hinweis",
  "hilfe": "Herkunft, Erhebungsmethode, Limitierungen (Markdown). Leer = ausgeblendet.",
  "format": { "typ": "markdown" },
  "default": "Die Werte stammen aus dem **amtlichen Datenbestand** und werden monatlich aktualisiert.",
  "erforderlich": "nein"
},
"datenStand": {
  "label": "Datenstand",
  "hilfe": "Freitext-Datum, z.B. 'Stand: Januar 2026'. Leer = ausgeblendet.",
  "format": { "typ": "string", "laenge": 60 },
  "default": "",
  "erforderlich": "nein"
},
"weiterfuehrendeLinks": {
  "label": "Weiterführende Links",
  "hilfe": "Verwandte Datensätze / Hintergrundquellen (Markdown). Leer = ausgeblendet.",
  "format": { "typ": "markdown" },
  "default": "- [Open Data Portal](https://opendata.example.org)\n- [Datensatz im Katalog](https://opendata.example.org/dataset/beispiel)",
  "erforderlich": "nein"
}

Die markdown-Defaults sind hier bereits in Markdown verfasst, konform zum oben beschriebenen Standard. Bestands-Apps verwenden in diesen Feldern noch HTML.

Ondics-Standard

Für Apps, die als Referenz, Showcase oder produktiver Ondics-Beitrag entstehen, gelten zusätzliche Qualitätsziele. Dazu gehören:

Diese Punkte sind nicht automatisch harte Anforderungen an alle externen Entwickler. Sie beschreiben unseren Qualitätsanspruch für vorzeigbare ODAS-Apps.


zurück zum Index