Local API
Import the generated API and call it directly without HTTP overhead. All queries and return types are fully typed based on your collection definitions.
import { cms } from "@/cms/.generated/api";Every collection is available as cms.<collection-slug>. For example, if you have collections posts, pages, and users:
cms.posts.find({ ... })cms.pages.findOne({ slug: "about" })cms.users.findById("abc123")const posts = await cms.posts.find({ where: { category: "tech" }, sort: { field: "_updatedAt", direction: "desc" }, limit: 10, offset: 0, status: "published", locale: "fi",});| Option | Type | Default | Description |
|---|---|---|---|
where |
Record<string, unknown> |
— | Filter by field values |
sort |
{ field, direction } |
— | Sort by field, "asc" or "desc" |
limit |
number |
— | Max documents to return |
offset |
number |
— | Skip N documents |
status |
"draft" | "published" | "scheduled" | "any" |
"published" (draft-enabled collections) / "any" (others) |
Filter by status |
locale |
string |
default locale | Language code for translations |
search |
string |
— | Substring search across text, slug, email, and select fields |
Find one
Section titled “Find one”const post = await cms.posts.findOne({ slug: "hello-world", locale: "fi", status: "any",});| Option | Type | Default | Description |
|---|---|---|---|
| field filters | varies | — | Filter by any field (e.g. slug, email) |
locale |
string |
default locale | Language code |
status |
"draft" | "published" | "scheduled" | "any" |
"published" (draft-enabled collections) / "any" (others) |
Status filter |
Find by ID
Section titled “Find by ID”const post = await cms.posts.findById("abc123", { locale: "fi", status: "any",});| Option | Type | Default | Description |
|---|---|---|---|
locale |
string |
default locale | Language code |
status |
"draft" | "published" | "scheduled" | "any" |
see below | Status filter |
findById filters by status only when status is passed explicitly: a value other than "any" returns null unless the document has that status. By default the document is returned regardless of status. For draft-enabled collections, the default also overlays the last-published snapshot values on the result (the same happens with an explicit "published"). Pass status: "any" to read the current working values without filtering.
Create
Section titled “Create”const post = await cms.posts.create({ title: "New Post", body: { type: "root", children: [...] }, _status: "published", // optional, defaults to "draft" if drafts enabled});Pass field values as properties. Returns the created document. You can supply your own _id to make imports idempotent; otherwise one is generated.
Create many
Section titled “Create many”const posts = await cms.posts.createMany([{ title: "A" }, { title: "B" }]);Runs the documents through create sequentially and returns the created documents. See Migrations for bulk-import patterns.
Upsert
Section titled “Upsert”await cms.posts.upsert({ _id: "abc123", title: "Hello" });Updates when a document with data._id exists, otherwise creates. Combined with caller-supplied _ids this makes imports re-runnable without a wipe-first step.
Update
Section titled “Update”const post = await cms.posts.update("abc123", { title: "Updated Title",});Only include fields you want to change. Returns the updated document.
Delete
Section titled “Delete”await cms.posts.delete("abc123");Cascades: removes translations, versions, and the document. Returns true if a row was removed, false if the document was not found. Auth collections refuse to delete the last remaining admin (the call throws).
Delete many
Section titled “Delete many”const removed = await cms.posts.deleteMany({ category: "tech" });Deletes every document matching the filter (all documents when the filter is omitted) and returns the number removed. Like discardDraft, it is runtime-only and not part of the generated typed API surface.
Publish / unpublish
Section titled “Publish / unpublish”await cms.posts.publish("abc123");await cms.posts.unpublish("abc123");Discard draft
Section titled “Discard draft”await cms.posts.discardDraft("abc123");Reverts a published document’s pending changes back to the last-published content. Only works on published documents that have been edited.
Note: discardDraft is currently runtime-only — it is not part of the generated typed API surface, so TypeScript will flag the call even though it works at runtime.
Schedule
Section titled “Schedule”await cms.posts.schedule( "abc123", "2025-06-01T00:00:00Z", // publishAt (required) "2025-07-01T00:00:00Z", // unpublishAt (optional));Sets status to "scheduled". On Cloudflare, a cron trigger processes scheduled publishing automatically. For Node.js, set up an external cron to call /api/cms/cron/publish. Set the CRON_SECRET env var to secure the endpoint.
const total = await cms.posts.count({ status: "published" });Accepts same filter options as find (without limit, offset, sort).
Versions
Section titled “Versions”const versions = await cms.posts.versions("abc123");// → [{ version: 5, createdAt: "...", snapshot: {...} }, ...]
await cms.posts.restore("abc123", 5);Translations
Section titled “Translations”const translations = await cms.posts.getTranslations("abc123");// → { fi: { title: "...", body: {...} } }
await cms.posts.upsertTranslation("abc123", "fi", { title: "Hei maailma", body: { type: "root", children: [...] },});upsertTranslation inserts or updates. Only include translatable fields.
Access context
Section titled “Access context”Every method accepts an optional context object as its last argument. Pass the signed-in admin
user to enforce field-level access, or _system: true to bypass all access
rules (for every operation: create/update/delete/read) in trusted server code such as public
API routes, task handlers, and seed scripts:
await cms.posts.find({}, { user });await cms["form-submissions"].create(data, { _system: true });_skipSearch: true is a runtime-only bulk-import flag that skips per-document search indexing. It is not part of the generated context type.
Introspection
Section titled “Introspection”cms.meta.getCollections(); // All collections with metadatacms.meta.getFields("posts"); // Field definitions for a collectioncms.meta.getCollection("posts"); // Full collection configcms.meta.getRouteForDocument("posts", doc); // Public URL for a documentcms.meta.getLocales(); // { default, supported }cms.meta.isTranslatableField("posts", "title"); // true/falsecms.meta.getConfig(); // Full CMS config objectBackground task helpers also exist as cms.tasks.enqueue, cms.tasks.drain, cms.tasks.tick, and cms.tasks.prune. See Background Tasks.
Usage in Astro pages
Section titled “Usage in Astro pages”---import { cms } from "@/cms/.generated/api";
const post = await cms.posts.findOne({ slug: Astro.params.slug });if (!post) return Astro.redirect("/404");
Astro.cache.set({ tags: ["posts", `post:${post._id}`] });---
<h1>{post.title}</h1>The raw tag array is equivalent to the cacheTags() helper used in Public Pages.