Beispiel-App: von null auf eine laufende Erweiterung

Eine vollständige, kleine App im Repository — Manifest, Server und Anbindung. Der kürzeste Weg zu einer Erweiterung, die wirklich läuft.

Für Entwickler

Voraussetzungen

  • Node.js 22
  • Eigener Server mit HTTPS
  • Angelegte App mit Zugangsdaten

Im Repository liegt unter `examples/extensions/menu-display` eine vollständige kleine App: Manifest, ein Node-Server und eine Beschreibung. Sie zeigt die Speisekarten-Metadaten eines Betriebs an und tut sonst nichts — genau deshalb ist sie als Vorlage brauchbar. Ein Beispiel, das gleich fünf Dinge tut, lehrt keines davon.

Was sie vorführt, ist der ganze Ablauf: Die App wird im Dashboard in einem Rahmen geladen. Sie fragt beim Elternfenster einen Startnachweis an, tauscht ihn gegen ein Zugriffstoken und ruft damit die REST-Schnittstelle auf. Der Startnachweis ist kurzlebig, das Zugriffstoken gilt 15 Minuten — nichts davon gehört in einen Browser-Speicher oder in ein Protokoll.

Drei Dinge müssen Sie ersetzen, bevor etwas läuft: die Adressen (alle Platzhalter unter `.example.invalid`), das Host-Land, und die Zugangsdaten, die Sie beim Anlegen der App bekommen. Die Zugangsdaten gehören ausschließlich in die Serverumgebung — nicht ins Manifest, nicht in den Browsercode, nicht in die Versionsverwaltung und nicht in einen Chat.

Die Rechtsangaben des Beispiels sind ebenfalls Platzhalter. Eine bestandene Schemaprüfung belegt weder Hosting noch Rechtskonformität: Datenschutzerklärung, Nutzungsbedingungen und Support-Seite müssen echte, erreichbare Seiten sein, bevor eine App in die Prüfung geht.

Schritt für Schritt

  1. Beispiel ansehen

    `examples/extensions/menu-display` — Manifest, Server und Beschreibung. Der begleitende Test prüft Verträge und Sicherheitsfälle ohne Netzwerk.

  2. App anlegen

    Als individuelle App im Betrieb (zum Ausprobieren) oder als öffentliche App im Agentur-Dashboard (zum Verkaufen).

  3. Adressen und Land ersetzen

    Alle `.example.invalid`-Adressen durch die eigene HTTPS-Domain, `hostingCountry` auf das tatsächliche Land.

  4. Zugangsdaten in die Serverumgebung

    Client-ID, Client-Geheimnis und Webhook-Geheimnis in eine private Umgebungsdatei außerhalb der Versionsverwaltung.

  5. Version anlegen und installieren

    Bei einer individuellen App ist die Version sofort einsatzbereit. Danach installieren und den Rechten zustimmen.

Codebeispiele

Startnachweis anfordern und gegen ein Zugriffstoken tauschen
AusschnittTypeScript
// Im Rahmen der App: Startnachweis beim Elternfenster anfragen.const nachweis = await new Promise<string>((auf, ab) => {    const nonce = crypto.randomUUID()    const uhr = setTimeout(() => ab(new Error('Kein Startnachweis erhalten')), 10_000)    window.addEventListener('message', function horcher(e) {        // Herkunft prüfen: eine Nachricht von irgendwoher ist kein Nachweis.        if (e.origin !== PARENT_ORIGIN) return        if (e.data?.type !== 'tt:token-response' || e.data.nonce !== nonce) return        clearTimeout(uhr)        window.removeEventListener('message', horcher)        auf(e.data.token)    })    window.parent.postMessage({ type: 'tt:token-request', nonce }, PARENT_ORIGIN)}) // Den Nachweis an den EIGENEN Server geben — nur dort liegt das Geheimnis.const res = await fetch('/api/sitzung', {    method: 'POST',    headers: { 'Content-Type': 'application/json' },    body: JSON.stringify({ nachweis }),})
Ausschnitt: läuft nicht für sich allein — er gehört an die passende Stelle einer bestehenden Vorlage.Läuft im Rahmen der App. Der Nachweis kommt vom Elternfenster, der Tausch passiert auf dem eigenen SERVER — das Client-Geheimnis darf den Browser nie erreichen.
Speisekarten lesen (Server, mit Zugriffstoken)
AusschnittTypeScript
const antwort = await fetch(`${APP_ORIGIN}/api/v1/menus`, {    headers: { Authorization: `Bearer ${zugriffstoken}` },})if (!antwort.ok) {    // 401 heißt abgelaufen — neu tauschen, nicht wiederholen.    // 403 heißt: das Recht fehlt. Das behebt kein Wiederholen, nur eine neue Zustimmung.    throw new Error(`Menüs nicht abrufbar: ${antwort.status}`)}const { data, hasMore } = await antwort.json()
Ausschnitt: läuft nicht für sich allein — er gehört an die passende Stelle einer bestehenden Vorlage.Das Zugriffstoken gehört in den Authorization-Kopf und bleibt auf dem Server. Die Antwort ist geblättert: `data`, `nextCursor`, `hasMore`, `limit`.