Jetzt mit KI-gestütztem Page Building über den MCP-Server

Routen und Seiten

Drei Request-Formen erreichen deine App und brauchen drei verschiedene Dinge. Das ist die Verdrahtung, die alle drei bedient.

Übernimm das als Ganzes. Die Teile hängen voneinander ab, und die Abhängigkeiten sind nicht offensichtlich.

Das mentale Modell

Drei Request-Formen erreichen deine App, und jede braucht etwas anderes:

  • Ein Besucher - veröffentlichte Inhalte, servergerendert, statisch wo möglich. Tempo, und das CMS bleibt aus dem Render-Pfad heraus.
  • Entwurfsvorschau (das /api/draft-Cookie) - Entwurfs-Inhalte auf der öffentlichen Route, ohne Editor. Jemand prüft eine Änderung, bearbeitet sie nicht.
  • Der Editor-Iframe (cmssyEdit=1 plus passendes cmssySecret) - Entwurfsinhalte plus Edit-Bridge, auf einer eigenen dynamischen Route.

Der dritte Punkt überrascht die meisten. Eine statische Seite sieht den Query-String nie, kann also nicht wissen, dass sie bearbeitet wird. Deshalb gibt es /cmssy-edit - und alles Folgende ergibt sich daraus.

1. Config

// cmssy.config.ts
import { defineCmssyConfig } from "@cmssy/next";

export const cmssy = defineCmssyConfig({
  org: process.env.CMSSY_ORG_SLUG,
  workspaceSlug: process.env.CMSSY_WORKSPACE_SLUG,
  draftSecret: process.env.CMSSY_DRAFT_SECRET,
});

Übergib process.env roh. Ein ?? ""-Fallback macht aus einer fehlenden Variable eine leere, und der Fehler taucht später an ganz anderer Stelle auf.

Dieses Modul liest Server-Env. Importiere daraus nie einen Wert - auch nicht aus einem Modul, das es importiert - in einer "use client"-Komponente. Typen sind unkritisch, sie werden gelöscht. Werte ziehen process.env ins Browser-Bundle.

2. Middleware

// proxy.ts
import { createCmssyProxy } from "@cmssy/next/middleware";
import { cmssy } from "@/cmssy.config";

export const proxy = createCmssyProxy(cmssy, {
  // Nur wenn deine URLs die Sprache tragen (/de/about) UND deine Routen
  // statische Pfade sind statt einer Catch-all-Route.
  stripLocalePrefix: true,
});

// Next parst das zur Compile-Zeit, der Matcher muss also ein Literal sein -
// eine importierte Konstante wird abgelehnt.
export const config = { matcher: ["/((?!_next/|api/|.*\\..*).*)"] };

Das Preset löst die Sprache auf, leitet verifizierten Editor-Traffic auf /cmssy-edit samt Sprache und Edit-Flag, setzt die CSP, die dem Admin das Framen deiner Site erlaubt, und entfernt ein Sprachpräfix, falls gewünscht - in genau dieser Reihenfolge.

Die Reihenfolge ist kein Detail, und jeder Weg, sie falsch zu machen, ist bereits einmal ausgeliefert worden:

  • Das Locale nach dem Rewrite auflösen - und die Editor-Vorschau rendert in der falschen Sprache; eine Route kann kein Präfix lesen, das sie nie sieht.
  • Das Edit-Flag vergessen - und Header und Footer kommen als Markup an, das der Editor auswählen, aber nicht füllen kann.

3. Die öffentliche Seite

Eine Catch-all-Route rendert jede veröffentlichte Seite. Die Metadaten kommen aus einem Helper, der dir gehört und auf der Delivery-API aufsetzt - das SDK liefert Daten, deine App besitzt ihre Routen:

// app/[[...path]]/page.tsx
import { createCmssyPage } from "@cmssy/next/server";
import { cmssy } from "@/cmssy/config";
import { blocks } from "@/cmssy/blocks";
import { buildPageMetadata } from "@/services/seo";

export const revalidate = 3600;
export const dynamicParams = true;

export async function generateMetadata({ params }) {
  const { path } = await params;
  // Wie geroutet, samt Präfix: das Präfix IST die Sprache.
  return buildPageMetadata(path);
}

export default createCmssyPage(cmssy, blocks);

buildPageMetadata gehört dir, nicht dem SDK. Es fragt die SEO-Felder der Seite ab und liefert ein Next.js-Metadata-Objekt - siehe SEO für den Inhalt.

4. Die Edit-Route

// app/cmssy-edit/[[...path]]/page.tsx
import { createCmssyEditPage } from "@cmssy/next/server";
import { cmssy } from "@/cmssy.config";
import { blocks } from "@/cmssy/blocks";
import { CmssyEditor } from "@/cmssy/editor";

export const dynamic = "force-dynamic";

export default createCmssyEditPage(cmssy, blocks, { editor: CmssyEditor });

Lässt du diese Datei weg, bleibt die Editor-Vorschau leer. Das ist die häufigste Art, eine cmssy-App zu brechen, und kein Build warnt dich - siehe Testen.

5. Die Editor-Bridge

// cmssy/editor.tsx
"use client";
import { CmssyLazyEditor } from "@cmssy/react/client";
import type { CmssyEditorProps } from "@cmssy/next";

export function CmssyEditor(props: CmssyEditorProps) {
  return <CmssyLazyEditor {...props} load={() => import("./blocks")} />;
}

Die Registry wird auf dem Client lazy geladen, damit deine Block-Loader - die serverseitig laufen und die Config lesen - nie ins Browser-Bundle geraten.

Caching

Veröffentlichen ist kein Deploy. Die öffentliche Route cached nach ihrem eigenen revalidate, und cmssy kann beim Veröffentlichen einen Revalidierungs-Webhook aufrufen:

export const revalidate = 3600;
export const dynamicParams = true;

Die Edit-Route ist die Ausnahme: Sie ist absichtlich force-dynamic, denn eine gecachte Edit-Seite würde den Entwurf von gestern zeigen.

Nächste Schritte

  • Layouts - Header und Footer als editierbare Blöcke.
  • Testen - beweisen, dass die Edit-Route noch funktioniert.
  • Wie cmssy funktioniert - was zwischen Request und Rendering passiert.
Routes and pages — mounting cmssy in a Next.js app