# Content API clients

> The content API clients in @uniformdev/canvas let you read Uniform content for frontend apps such as Next.js, and write content from automations, CLIs, and backend services.

Source: https://docs.uniform.app/docs/sdk/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](https://docs.uniform.app/docs/guides/automations) 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](https://docs.uniform.app/docs/api). 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](https://docs.uniform.app/docs/api). Use them from automations, CLIs, and backend services.

## The clients

| Client | Mode | Methods | Host / shape |
| --- | --- | --- | --- |
| `CompositionDeliveryClient` | delivery | `get` `list` | edge, resolved |
| `CompositionManagementClient` | management | `get` `list` `save` `saveAndPublish` `unpublish` `remove` `history` | origin, canonical |
| `EntryDeliveryClient` | delivery | `get` `list` | edge, resolved |
| `EntryManagementClient` | management | `get` `list` `save` `saveAndPublish` `unpublish` `remove` `history` | origin, canonical |
| `ComponentDefinitionClient` | management | `get` `list` `save` `remove` | origin |
| `ContentTypeClient` | management | `get` `list` `save` `remove` | origin |
| `RouteClient` | delivery | `get` | edge, resolved |

## Constructing a client

Supply `projectId` and either `apiKey` or `bearerToken`:

```ts
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.

## Reading

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

```ts
// 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](https://docs.uniform.app/docs/guides/composition/editions) 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

Writing is available on the management clients only.

```ts
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)
```

> **Use an AI coding assistant:**
>
> The [`uniform-sdk`](https://github.com/uniformdev/agent-skills/tree/main/skills/uniform-sdk) agent skill teaches coding assistants such as Claude Code, Codex, Cursor, and Copilot how to authenticate with Uniform, configure the CLI, resolve routes, and read and write compositions, entries, and content types with the content API clients. To install it, see [Uniform agent skills](https://docs.uniform.app/docs/guides/ai/agent-skills).

## Migrating from the old clients

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.

| Old | New |
| --- | --- |
| `CanvasClient` (compositions) | `CompositionDeliveryClient` + `CompositionManagementClient` |
| `CanvasClient` (component definitions) | `ComponentDefinitionClient` |
| `ContentClient` (entries) | `EntryDeliveryClient` + `EntryManagementClient` |
| `ContentClient` (content types) | `ContentTypeClient` |
| `Uncached*Client` | `bypassCache: true` |
| `RouteClient.getRoute` | `RouteClient.get` (same client, renamed method) |

### Delivery read

```ts
// 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' });
```

### Read, modify, write

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

```ts
// 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,
});
```

### Content types and component definitions

```ts
// before
const content = new ContentClient({ apiKey, projectId });
await content.upsertContentType({ contentType });

// after
const contentTypes = new ContentTypeClient({ apiKey, projectId });
await contentTypes.save({ contentType });
```

## Renamed methods on the other clients

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.

| Client | Deprecated → new |
| --- | --- |
| `CategoryClient` | `getCategories` → `list`, `upsertCategories` → `save`, `removeCategory` → `remove` |
| `LabelClient` | `getLabels` → `list`, `upsertLabel` → `save`, `removeLabel` → `remove` |
| `ProjectClient` | `getProjects` → `list`, `upsert` → `save`, `delete` → `remove` |
| `DataSourceClient` | `getList` → `list`, `upsert` → `save` |
| `DataTypeClient` | `get` → `list`, `upsert` → `save` |
| `LocaleClient` | `get` → `list`, `upsert` → `save` |
| `WorkflowClient` | `get` → `list`, `upsert` → `save` |
| `ReleaseClient` | `get` → `list`, `upsert` → `save` |
| `RelationshipClient` | `get` → `list` |
| `ReleaseContentsClient` | `get` → `list` |
| `EntityReleasesClient` | `get` → `list` |

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

## Using the clients from an automation

An [automation](https://docs.uniform.app/docs/guides/automations/code-automations) 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:

```ts
const compositions = new CompositionManagementClient(context.uniformCredentials);
```
