Content API clients

The Content API clients in @uniformdev/canvas let you read and write Uniform content (compositions, entries, component definitions, content types, and routes). The most common use case is reading content for frontend applications such as Next.js apps. You can also call them from any server-side context: an automation handler, a CLI, a script, or a backend service.

Pick a delivery or a management client based on what you need to do:

  • Delivery clients fetch content from Uniform's Edge Delivery APIs. Use them to render or serve compositions, entries, and routes. They are read-only.
  • Management clients create, update, publish, and delete content through Uniform's Management APIs. Use them from automations, CLIs, and backend services.
ClientModeMethodsHost / shape
CompositionDeliveryClientdeliveryget listedge, resolved
CompositionManagementClientmanagementget list save saveAndPublish unpublish remove historyorigin, canonical
EntryDeliveryClientdeliveryget listedge, resolved
EntryManagementClientmanagementget list save saveAndPublish unpublish remove historyorigin, canonical
ComponentDefinitionClientmanagementget list save removeorigin
ContentTypeClientmanagementget list save removeorigin
RouteClientdeliverygetedge, resolved

Supply projectId and either apiKey or bearerToken:

import { CompositionManagementClient, CompositionDeliveryClient } from '@uniformdev/canvas'; const management = new CompositionManagementClient({ apiKey, projectId }); const delivery = new CompositionDeliveryClient({ apiKey, projectId });

Delivery clients additionally accept edgeApiHost (defaults to https://uniform.global) and disableSWR, which sends x-disable-swr to skip stale-while-revalidate on data-resource caches.

Select what to read with a selector, then layer read options on top.

// compositions: by id (+ optional editionId / versionId), slug, or project map node path await compositions.get({ compositionId, editionId }); await compositions.get({ slug: '/home' }); // entries: by id (+ optional editionId / versionId) or slug await entries.get({ entryId }); // list returns a page of results, and takes filtering and paging options // alongside the same read options as get await entries.list({ limit: 10 });
  • state is optional and has a per-mode default. Delivery defaults to published, management defaults to draft.
  • Editions are derived, not asked for. An edition is a locale-targeted variant of a composition or entry. On a management get, a bare id returns that composition or entry (raw). Passing a locale resolves the locale-active edition (auto). Passing an editionId fetches that edition. That way a later save targets the same entity you just read. You can force editions: 'raw' | 'auto' if you need to. On list, pass the full editions: 'auto' | 'all' | 'raw' enum.

Writing is available on the management clients only.

const { modified } = await entries.save(entryBody); // create/update the draft await entries.saveAndPublish(entryBody); // draft + publish in one call await entries.unpublish({ entryId }); // drop the published state await entries.remove({ entryId }); // delete the whole thing await entries.remove({ entryId, editionId }); // delete just one edition (all its states)

The first migration decision for every call site is delivery or management? If the code reads to render or serve, use delivery. If it reads in order to mutate and save, or writes at all, use management.

OldNew
CanvasClient (compositions)CompositionDeliveryClient + CompositionManagementClient
CanvasClient (component definitions)ComponentDefinitionClient
ContentClient (entries)EntryDeliveryClient + EntryManagementClient
ContentClient (content types)ContentTypeClient
Uncached*ClientbypassCache: true
RouteClient.getRouteRouteClient.get (same client, renamed method)
// before const canvas = new CanvasClient({ apiKey, projectId }); const composition = await canvas.getCompositionBySlug({ slug: '/home', state: CANVAS_PUBLISHED_STATE }); // after: delivery client defaults state to published const compositions = new CompositionDeliveryClient({ apiKey, projectId }); const composition = await compositions.get({ slug: '/home' });

For example, an automation reacting to a draft event. The old approach needed six options to avoid losing data:

// before const canvas = new UncachedCanvasClient({ bearerToken, projectId }); const composition = await canvas.getCompositionById({ compositionId: event.editionId ?? event.compositionId, editions: 'raw', releaseId: event.releaseId, state: CANVAS_DRAFT_STATE, skipDataResolution: true, skipPatternResolution: true, skipOverridesResolution: true, withComponentIDs: true, }); // ...mutate... await canvas.updateComposition({ composition: mutated }); // save draft await canvas.updateComposition({ composition: mutated, state: CANVAS_PUBLISHED_STATE }); // publish // after: the management client is canonical, draft, and fresh by construction const compositions = new CompositionManagementClient({ bearerToken, projectId }); const composition = await compositions.get({ compositionId: event.compositionId, editionId: event.editionId, releaseId: event.releaseId, }); // ...mutate... await compositions.saveAndPublish({ composition: composition.composition, editionId: event.editionId, releaseId: event.releaseId, });
// before const content = new ContentClient({ apiKey, projectId }); await content.upsertContentType({ contentType }); // after const contentTypes = new ContentTypeClient({ apiKey, projectId }); await contentTypes.save({ contentType });

The remaining clients keep their class but standardized their verbs on the same list, save, and remove vocabulary. Only the method names changed, and the old names remain as @deprecated aliases.

ClientDeprecated → new
CategoryClientgetCategorieslist, upsertCategoriessave, removeCategoryremove
LabelClientgetLabelslist, upsertLabelsave, removeLabelremove
ProjectClientgetProjectslist, upsertsave, deleteremove
DataSourceClientgetListlist, upsertsave
DataTypeClientgetlist, upsertsave
LocaleClientgetlist, upsertsave
WorkflowClientgetlist, upsertsave
ReleaseClientgetlist, upsertsave
RelationshipClientgetlist
ReleaseContentsClientgetlist
EntityReleasesClientgetlist

Note the list-returning getlist renames. get now consistently means a single fetch.

An automation can call these clients as its own Uniform identity. Pass context.uniformCredentials to the client constructor. It carries the projectId and bearerToken the clients expect:

const compositions = new CompositionManagementClient(context.uniformCredentials);