Content
Content Overview
Content methods read and write entries under /{collectionSlug} and bulk routes. They use your API key (CMS routes), not end-user tokens.
const noma = createClient({ projectId, apiKey })Entry bodies use a data object keyed by each field’s name from the schema (usually kebab-case, e.g. meta-title). System fields such as locale and published_at sit next to data on create and update. state is accepted on create only — saves never change publish state.
Saves are separate from publish/unpublish
NomaCMS uses a classic working-draft + published-snapshot model:
content.update/content.patch/content.bulkUpdatemutate only the draft. They never mint a version and never flip the entry's public visibility.content.publish(collectionSlug, uuid)mints a new immutable snapshot from the current draft and makes it live understate: "published".content.unpublish(collectionSlug, uuid)takes the entry offline fromstate: "published"reads without deleting any versions.- On
content.create, passingstate: "published"is still a valid one-step flow (publish-on-create).
Draft vs. published reads
NomaCMS keeps an immutable snapshot every time an entry is published (see Versions). The state option on content.list / content.get chooses which source to read from:
state: "published"(default) reads the latest published snapshot. Entries that have never been published, or were later unpublished, are excluded.state: "draft"reads the live working-draft field values.
Editing an entry does not immediately change what state: "published" consumers see — only content.publish does.
content.list
Query entries in a collection.
const result = await noma.content.list("blog-posts", {
state: "published",
locale: "en",
sort: "created_at:DESC",
paginate: 20,
page: 1,
})| Param | Type | Description |
|---|---|---|
state | 'draft' | 'published' | Filter by draft or published entries. |
locale | string | Locale code. |
exclude | string | string[] | Field names to omit from each entry. |
where | Record<string, unknown> | Structured filters; serialized to where[field][...] query params. |
sort | string | Comma-separated sort keys. Each key is column:ASC or column:DESC (for example created_at:DESC, title:ASC). Core columns include id, created_at, updated_at, published_at; custom field names sort when paginate is set. |
limit | number | Max rows (non-paginated list). |
offset | number | Skip rows (only applied when limit is set). |
paginate | number | Page size when using page. |
page | number | Page number. |
timestamps | boolean | Include created_at / updated_at on each entry when supported. |
count | flag | If the count query parameter is present, the response is { count: number } for the current filters instead of a list of entries. |
first | flag | If the first query parameter is present on a list collection, the response is a single entry object (first row). Singleton collections use a different code path and ignore this. |
Singleton collections
If the collection has is_singleton: true, list returns one entry resource directly (not an array), before pagination. That is separate from the first query parameter, which applies to non-singleton collections when you want only the first matching row.
where filters
Pass a nested object; the SDK flattens it for query strings. Detailed operators (equals, in, relations, and so on) are documented in the Content API filtering reference.
content.get
Fetch a single entry by UUID.
const entry = await noma.content.get("blog-posts", entryUuid, {
locale: "en",
translation_locale: "tr",
state: "published",
})| Param | Type | Description |
|---|---|---|
locale | string | Preferred locale for the entry. |
translation_locale | string | Load a linked translation in this locale when available. |
state | 'draft' | 'published' | Which revision to read. |
exclude | string | string[] | Fields to strip from the response. |
timestamps | boolean | Include timestamps when supported. |
content.create
Create an entry.
await noma.content.create("blog-posts", {
locale: "en",
state: "draft",
data: {
title: "Hello",
slug: "hello",
content: "<p>...</p>",
},
})| Field | Description |
|---|---|
locale | Optional locale for the new entry. |
state | draft or published. Use the built-in state instead of a custom "published" boolean field. |
published_at | Optional ISO timestamp when publishing. |
data | Required. Field values keyed by field name. |
content.update
Full replacement of an entry's draft (PUT semantics on the API). Saves never change publish state — call content.publish afterwards to make edits live.
await noma.content.update("blog-posts", entryUuid, {
data: {
title: "Hello (updated)",
slug: "hello",
content: "<p>Updated</p>",
},
})
await noma.content.publish("blog-posts", entryUuid)content.patch
Partial update (PATCH). Send only the fields you want to change inside data, plus optional locale / published_at.
await noma.content.patch("blog-posts", entryUuid, {
data: {
title: "New title only",
},
})content.publish
Mint a new immutable version from the current draft and make it the live snapshot served under state: "published".
const { version_number } = await noma.content.publish("blog-posts", entryUuid)content.unpublish
Clear the live pointer so state: "published" reads return 404. Versions are retained and still accessible via content.versions.*.
await noma.content.unpublish("blog-posts", entryUuid)content.delete
Delete an entry.
await noma.content.delete("blog-posts", entryUuid)
await noma.content.delete("blog-posts", entryUuid, true) // force when API allowsThe third argument sets force on the query string when you need hard deletes per API rules.
Bulk operations
content.bulkCreate
await noma.content.bulkCreate("blog-posts", {
items: [
{ locale: "en", state: "draft", data: { title: "A", slug: "a" } },
{ locale: "en", state: "draft", data: { title: "B", slug: "b" } },
],
})content.bulkUpdate
Like content.update, bulk updates only mutate drafts; state is not a field here. Call content.publish per UUID afterwards if you want the edits to go live.
await noma.content.bulkUpdate("blog-posts", {
items: [
{ uuid: uuid1, data: { title: "A2" } },
{ uuid: uuid2, data: { title: "B2" } },
],
})content.bulkDelete
await noma.content.bulkDelete("blog-posts", {
uuids: [uuid1, uuid2],
force: false,
})content.versions
Every publish creates a new immutable snapshot. The versions namespace lets you list, inspect, revert, and label those snapshots.
content.versions.list
List all versions of an entry (most recent first).
const { data } = await noma.content.versions.list("blog-posts", entryUuid)content.versions.get
Fetch a specific version by its integer version_number, including the full snapshot payload.
const v3 = await noma.content.versions.get("blog-posts", entryUuid, 3)
// v3.snapshot.fields -> values keyed by field namesnapshot.fields mirrors the same shape accepted by content.create / content.update: primitive scalars, UUID arrays for media, UUID or ID arrays for relations, and nested arrays for group fields.
content.versions.revert
Restore the working draft from a prior snapshot and publish it as a new version (vNext). Older versions are preserved, subject to plan retention.
const result = await noma.content.versions.revert("blog-posts", entryUuid, 3)
// result.message, result.version, result.entrycontent.versions.updateLabel
Edit the label and/or description of a version. The snapshot stays immutable.
await noma.content.versions.updateLabel("blog-posts", entryUuid, 3, {
label: "Launch copy",
description: "Approved by marketing.",
})Version retention is plan-based (Basic 10 / Grow 50 / Pro unlimited). When the cap is reached, the oldest versions are pruned on the next publish — the currently published version is never pruned. See Versions.
content.linkTranslation
Link two entries in the same collection as translations (different locales).
import type { LinkTranslationPayload } from "@nomacms/js-sdk"
const payload: LinkTranslationPayload = {
translation_entry_uuid: otherEntryUuid,
}
await noma.content.linkTranslation("blog-posts", entryUuid, payload)Related
- Assets - Upload files referenced from media fields.
- Error Handling - Validation and not-found errors from these calls.