API Client
import { createKenresoftClient, KenresoftApiError } from '@kenresoft-cms/astro';
export const cms = createKenresoftClient({ url: import.meta.env.PUBLIC_KENRESOFT_CMS_URL,});| Option | Purpose |
|---|---|
url |
The CMS API base URL (required). Use '/cms' in the browser behind the proxy |
previewToken |
Binds the client to one request’s Live Preview token |
cookies |
The incoming request’s cookie header, so server-side calls know who is signed in |
fetch |
Override the global fetch (testing). cookies is ignored when you supply one |
The package is a thin fetch wrapper with no Astro dependency. It is meant to be used through a bundler (Astro, Vite, esbuild) with TypeScript’s moduleResolution set to Bundler, which is the default in Astro projects. Importing its built files directly from plain Node.js ESM is not supported. It talks only to public, auth and signed-in account routes, never the admin API.
Entries
Section titled “Entries”const posts = await cms.entries.list({ contentType: 'blog-post' }); // Entry[] ([] if none)const featured = await cms.entries.list({ contentType: 'blog-post', featured: true }); // only Featured entriesconst post = await cms.entries.get({ contentType: 'blog-post', slug }); // Entry | nullOnly published entries are returned. A missing slug and a draft both resolve to null, identical from the outside. Any other non-2xx response throws KenresoftApiError. entries.preview({ contentType, slug, token }) fetches with an explicit preview token.
cms.media.url({ id }); // string: URL of the file, no request madeawait cms.media.get({ id }); // { altText, contentType, width, height } | nullawait cms.media.byFolder({ slug }); // items in a media folder ([] if the folder is unknown)try { await cms.forms.submit({ formSlug: 'contact', data: { name: 'Ada', message: 'Hi' } });} catch (err) { if (err instanceof KenresoftApiError && err.issues) { // err.issues: { path: (string | number)[], message: string }[] }}submit sends a plain object as JSON. For a form with a file field, pass a FormData instead and it is sent as multipart/form-data:
const data = new FormData(formElement); // includes the <input type="file" name="document">await browserCms.forms.submit({ formSlug: 'support', data });To pass a Turnstile token for a form with the human check, add turnstileToken: cms.forms.submit({ formSlug, data, turnstileToken }). On a failure, KenresoftApiError.code is set when the API sends one (for example TURNSTILE_REQUIRED). turnstileToken and code need @kenresoft-cms/astro 0.10.0 or later.
It throws for any non-2xx response: 400 (with issues, or a TURNSTILE_REQUIRED code), 401 (the form requires an account and nobody is signed in), 403 (untrusted Origin), 404, 409 (the form refuses duplicates and this one was already submitted, or an entry the form is about is closed), 429, or 500 (a file couldn’t be stored; nothing was kept, so it’s safe to retry). It sends the session cookie, so for a form that requires an account call it from the browser through the proxy. FormData needs @kenresoft-cms/astro 0.8.0 or later; older versions only send JSON.
Account submissions
Section titled “Account submissions”For forms that require a website account, the signed-in account’s own submissions. Every call is scoped to the session’s account: another account’s submission id behaves exactly like one that doesn’t exist.
await browserCms.account.submissions.list({ form: 'support' }); // AccountSubmissionSummary[] (form optional)await browserCms.account.submissions.get({ id }); // AccountSubmissionDetail | nullbrowserCms.account.submissions.fileUrl({ id, mediaId }); // string: a download link, no request madeawait browserCms.account.submissions.sendMessage({ id, body, files }); // AccountSubmissionMessageA detail has the submission’s data, stage, stages, stageHistory, messages (each from: 'staff' | 'account', with its attachments) and files (the files submitted with the form, keyed by field name). The inbox status, test flag and staff email addresses are never included. sendMessage takes up to 5 Files; it needs a trusted Origin, so call it from browser code through the proxy. fileUrl downloads only for the signed-in owner.
Server-side, use cmsForRequest(Astro.request).account.submissions.get({ id }) to render the same data with the visitor’s cookie. Requires @kenresoft-cms/astro 0.8.0 or later.
Pages and routing
Section titled “Pages and routing”await cms.pages.list(); // { id, route, title }[] of published pagesawait cms.pages.resolve({ route: '/about' }); // Page | null (previewToken honored)await cms.pages.preview({ route, token });await cms.reusableBlocks.get({ id }); // ReusableBlock | nullawait cms.routePatterns.list(); // { contentTypeSlug, routePattern }[]See Rendering pages.
Site data
Section titled “Site data”await cms.settings.general(); // also contact(), social(), navigation(), footer(), seo()await cms.globalVariables.list(); // Record<string, string>, {} if noneawait cms.system.status(); // { emailConfigured, authSecretConfigured, turnstileSiteKey }Each settings module resolves its typed shape, or {} (never null) if it has never been saved. See Content model. system.status() is unauthenticated and deployment-wide — turnstileSiteKey is the main reason a frontend calls it: read the Turnstile site key from here instead of needing your own copy in a PUBLIC_ env var. See Authentication.
Authentication and Commerce
Section titled “Authentication and Commerce”cms.auth is documented in Authentication. cms.commerce (categories, products, cart, checkout, payments, customer) exists for the Commerce plugin and returns 404 errors when it is not installed or enabled.
Errors
Section titled “Errors”KenresoftApiError has status, message, optional issues (form validation) and optional code (a machine-readable code from Better Auth, such as EMAIL_NOT_VERIFIED). See Error codes.
Helpers
Section titled “Helpers”| Export | Purpose |
|---|---|
getPreviewToken(input) |
Reads preview_token from a URL, URL string or Request. Returns null when absent |
createCmsProxy(options) |
Same-origin proxy handler. See proxy |
resolveRoute, resolveSiteRoute, matchRoutePattern |
Pure URL-to-content resolution |
resolveNavigationItems(items, pages) |
Resolves a navigation item’s page reference to its route |
registerBlockRenderer, resolveBlockRenderer |
Developer override registry for block components |
renderField, registerFieldRenderer, resolveFieldRenderer |
Read-only display shapes for field values |
There is deliberately no contentTypes.list(): the public API does not expose field definitions.