API de entrega GraphQL

Qué consultas existen, cómo funciona el acotado por workspace y cuáles envuelve el SDK frente a las que escribes tú.

cmssy sirve contenido publicado por un único endpoint GraphQL. El SDK ya envuelve las lecturas habituales -páginas, layouts, configuración del sitio, formularios-, así que la mayoría de apps nunca escribe una consulta. Para lo demás (modelos propios, registros, listar páginas hijas) envías la tuya por el cliente de entrega.

Endpoint y acotado

Las lecturas públicas van a la ruta con organización:

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

apiBase es tu apiUrl sin el /graphql final: por defecto https://api.cmssy.io, así que las peticiones aterrizan en https://api.cmssy.io/public/{org}/{ws}/graphql. Sobrescribe apiUrl solo si te auto-alojas. org y workspaceSlug vienen de tu configuración y el SDK arma la ruta por ti.

Como la organización va en la ruta, un slug de workspace solo tiene que ser único dentro de su organización.

Dos formas de acotar una consulta

Toda operación está acotada al workspace, pero no todas del mismo modo. Este es el detalle con el que la gente tropieza:

  • workspaceSlug (String!): para lecturas de páginas, layouts, configuración y formularios. Los helpers del SDK lo pasan automáticamente desde tu configuración.
  • workspaceId (String!): para lecturas de modelos, registros y páginas por tipo. Llama a client.queryScoped(...): si tu consulta declara $workspaceId y no lo pasas, el SDK lo resuelve desde workspaceSlug e inyecta la variable y la cabecera x-workspace-id.
// $workspaceId se rellena por ti
await client.queryScoped(MY_QUERY, { modelSlug: "products", limit: 20 });

Lo que el SDK ya lee por ti

Normalmente nunca escribes estas: createCmssyPage, CmssyServerLayout y la resolución de formularios detrás de context.forms las emiten internamente.

  • public.page.get: una página por slug, { id, blocks, publishedBlocks } más los campos SEO seoTitle, seoDescription, seoKeywords, displayName.
  • public.page.getById: { id, publishedBlocks }.
  • public.page.list: [{ id, slug, updatedAt, publishedAt }].
  • public.page.layouts: [{ position, blocks, settings }].
  • public.siteConfig: nombre del sitio, idioma por defecto e idiomas activos, funciones, branding.
  • public.form.get: campos y ajustes del formulario, expuestos a los bloques como context.forms.
  • public.form.submit: { success, message, submissionId, redirectUrl }.

Los helpers que las envuelven -fetchPage, fetchPages, fetchLayouts, fetchPageMeta, fetchSiteConfig, resolveSiteLocales, resolveForms- viven bajo @cmssy/core/internal y no son API pública. La superficie pública es createCmssyClient y graphqlRequest: todo lo que createCmssyPage no renderice ya, lo consultas tú.

Estas las escribes tú

No tienen helper en el SDK. Envíalas por client.queryScoped(...).

Registros de modelos propios

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
      }
    }
  }
}

Escribe la consulta tú y guárdala en tu repositorio. El SDK sí lleva cadenas equivalentes, pero bajo @cmssy/core/internal: una subruta interna, lo que significa que puede cambiar entre versiones sin nota de cambio incompatible. Tu propia consulta ocupa cuatro líneas y nunca te sorprende.

Listar páginas hijas

Esto es lo que alimenta un índice de blog o un árbol de documentación:

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

byType acepta además pageType, sortBy y customFieldFilters, y -con un previewSecret válido- includeDrafts.

Enviar un formulario

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

Pasa { formId, input: { data } }. El SDK lleva la misma cadena como SUBMIT_FORM_MUTATION, pero bajo @cmssy/core/internal: guárdate tu propia copia en lugar de importarla.

La auth de miembros la montas tú

Las mutaciones siteMember -login, register, refresh, logout, forgotPassword, resetPassword, verifyEmail- sostienen el flujo de autenticación de miembros.

El SDK slim no incluye ruta de auth ni lector de sesión. login y refresh te devuelven los accessToken y refreshToken en crudo, y el backend no establece ninguna cookie: la sesión es de tu aplicación. Una cookie httpOnly escrita por tu propio route handler es la opción segura por defecto. El flujo completo, del registro a la verificación de correo, está en autenticación de miembros.

Cosas que conviene saber

  • data y filter usan el escalar JSON: pasa objetos planos, no JSON en cadena.
  • customFields en una página es un mapa JSON de los campos propios de ese tipo de página.
  • Las lecturas devuelven solo contenido publicado, salvo que se aporte un previewSecret válido. El SDK lo hace por ti en modo edición con tu draftSecret.

Siguientes pasos