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}/graphqlapiBase 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 aclient.queryScoped(...): si tu consulta declara$workspaceIdy no lo pasas, el SDK lo resuelve desdeworkspaceSluge inyecta la variable y la cabecerax-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 SEOseoTitle,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 comocontext.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
datayfilterusan el escalarJSON: pasa objetos planos, no JSON en cadena.customFieldsen una página es un mapaJSONde los campos propios de ese tipo de página.- Las lecturas devuelven solo contenido publicado, salvo que se aporte un
previewSecretválido. El SDK lo hace por ti en modo edición con tudraftSecret.
Siguientes pasos
- Servidor MCP: la vía de escritura.
- Loaders de servidor: donde suelen vivir las consultas propias.
- Autenticación de miembros: el flujo tras las mutaciones
siteMember.