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

GraphQL-Delivery-API

Welche Queries es gibt, wie Workspace-Scoping funktioniert und welche Reads das SDK kapselt statt dich schreiben zu lassen.

cmssy liefert veröffentlichte Inhalte über einen einzigen GraphQL-Endpunkt. Das SDK kapselt die üblichen Reads bereits - Seiten, Layouts, Site-Config, Formulare - die meisten Apps schreiben also nie eine Query. Für alles andere (eigene Modelle, Records, Kindseiten auflisten) schickst du deine eigene über den Delivery-Client.

Endpunkt und Scoping

Öffentliche Reads gehen an den org-basierten Pfad:

{apiBase}/public/{orgSlug}/{workspaceSlug}/graphql

apiBase ist dein apiUrl ohne das abschließende /graphql - standardmäßig https://api.cmssy.io, Anfragen landen also auf https://api.cmssy.io/public/{org}/{ws}/graphql. Überschreibe apiUrl nur beim Self-Hosting. org und workspaceSlug stammen aus deiner Config, den Pfad baut das SDK.

Weil die Organisation im Pfad steht, muss ein Workspace-Slug nur innerhalb seiner Organisation eindeutig sein.

Zwei Arten, eine Query zu scopen

Jede Operation ist workspace-gebunden, aber nicht alle auf dieselbe Weise. An diesem Detail stolpern die meisten:

  • workspaceSlug (String!) - für Seiten-, Layout-, Config- und Formular-Reads. Die Fetch-Helper des SDK übergeben ihn automatisch aus deiner Config.
  • workspaceId (String!) - für Modell-, Record- und Page-by-Type-Reads. Rufe client.queryScoped(...): Deklariert deine Query $workspaceId und du übergibst ihn nicht, löst das SDK ihn aus workspaceSlug auf und ergänzt Variable und x-workspace-id-Header.
// $workspaceId wird für dich gesetzt
await client.queryScoped(MY_QUERY, { modelSlug: "products", limit: 20 });

Was das SDK bereits kapselt

Diese schreibst du normalerweise nie - der genannte Helper ruft sie für dich auf.

  • publicPagefetchPage - liefert { id, blocks, publishedBlocks }.
  • publicPageByIdfetchPageById - liefert { id, publishedBlocks }.
  • publicPagesfetchPages - liefert [{ id, slug, updatedAt, publishedAt }].
  • publicPage (SEO-Felder) → fetchPageMeta - liefert { id, seoTitle, seoDescription, seoKeywords, displayName }.
  • publicPageLayoutsfetchLayouts - liefert [{ position, blocks }].
  • publicSiteConfigfetchSiteConfig / resolveSiteLocales - Site-Name, Locales, Features, Branding.
  • public.form.getresolveForms (und context.forms) - Formularfelder und -einstellungen.
  • public.form.submitSUBMIT_FORM_MUTATION - liefert { success, message, submissionId, ... }.

Diese schreibst du selbst

Dafür gibt es keinen SDK-Helper. Schicke sie über client.queryScoped(...).

Records eigener Modelle

query PublicModelRecords(
  $workspaceId: String!
  $modelSlug: String!
  $filter: JSON
  $sort: String
  $limit: Int
  $offset: Int
  $populate: [String!]
) {
  public {
    model {
      records(
        workspaceId: $workspaceId
        modelSlug: $modelSlug
        filter: $filter
        sort: $sort
        limit: $limit
        offset: $offset
        populate: $populate
      ) {
        items { id modelId data status createdAt updatedAt }
        total
        hasMore
      }
    }
  }
}

Schreib die Query selbst und halte sie in deinem Repo. Das SDK führt zwar entsprechende Strings, aber unter @cmssy/core/internal - einem internen Subpfad, der sich zwischen Releases ohne Breaking-Change-Hinweis ändern darf. Deine eigene Query ist vier Zeilen lang und überrascht dich nie.

Kindseiten auflisten

Das treibt einen Blog-Index oder einen Docs-Baum an:

query PublicPagesByType(
  $workspaceId: String!
  $parentSlug: String
  $search: String
  $limit: Int
  $offset: Int
) {
  publicPagesByType(
    workspaceId: $workspaceId
    parentSlug: $parentSlug
    search: $search
    limit: $limit
    offset: $offset
  ) {
    items {
      id
      slug
      fullSlug
      publishedAt
      displayName
      seoTitle
      seoDescription
      customFields
      pageType
    }
    total
    hasMore
  }
}

Ein Formular absenden

mutation SubmitForm($formId: ID!, $input: SubmitFormInput!) {
  public {
    form {
      submit(formId: $formId, input: $input) {
        success
        message
        submissionId
        redirectUrl
      }
    }
  }
}

Exportiert als SUBMIT_FORM_MUTATION. Übergib { formId, input: { data } }.

Member-Auth: nicht direkt aufrufen

Die siteMember-Mutationen - login, register, refresh, logout, forgotPassword, resetPassword, verifyEmail - tragen den Member-Auth-Flow.

Rufe sie nicht aus deinem eigenen Code auf. Mounte stattdessen createCmssyAuthRoute: Sie werden serverseitig behandelt und das Session-Cookie versiegelt. Direkte Aufrufe heißen, das Token-Sealing selbst zu übernehmen - und ein Fehler dabei ist ein Sicherheitsbug, keine kaputte Seite.

Wissenswertes

  • data und filter nutzen den JSON-Skalar - übergib einfache Objekte, kein stringifiziertes JSON.
  • customFields einer Seite ist eine JSON-Map der Custom Fields ihres Seitentyps.
  • Reads liefern nur veröffentlichte Inhalte, sofern kein gültiges previewSecret mitkommt. Im Edit-Modus erledigt das SDK das mit deinem draftSecret.

Nächste Schritte