Headless CMS > Using the Webiny SDK
Using the Webiny SDK
Practical guide to querying and mutating Headless CMS content using the Webiny SDK
- How to define TypeScript types for your content models?
- How to query entries using the Webiny SDK?
- How to create, update, and delete entries?
- How to handle errors with the Result pattern?
Overview
This guide covers practical examples of working with Headless CMS content using the Webiny SDK. The SDK is the recommended way to interact with a Webiny instance from external JavaScript or TypeScript applications such as Next.js, Vue, SvelteKit, or Node.js scripts. It provides type-safe methods for querying and mutating content, with automatic API selection and built-in error handling.
Prerequisites
- Webiny SDK initialized in your project
- API key with appropriate permissions (read-only or full-access)
- At least one content model created in Headless CMS
Defining TypeScript Types
Define interfaces that match your content model structure. Field names must match the field IDs (not labels) from your content model:
import type { CmsEntryData } from "@webiny/sdk";
export interface Product {
name: string;
description: string;
price: number;
sku: string;
category?: CmsEntryData<ProductCategory>;
}
export interface ProductCategory {
name: string;
slug: string;
}Key points:
- Field names match field IDs from content model
- Reference fields use
CmsEntryData<T>type - Optional fields use
?suffix
Pass your interface as a generic to SDK methods for full type safety:
// ✅ Type-safe - product.values is typed as Product
const result = await sdk.cms.getEntry<Product>({
modelId: "product",
entryId: "abc123",
fields: ["id", "entryId", "values.name", "values.price"]
});
if (result.isOk()) {
const name = result.value.values.name; // string (autocomplete works)
const price = result.value.values.price; // number
}
// ❌ No type safety - product.values is any
const result = await sdk.cms.getEntry({
modelId: "product",
entryId: "abc123",
fields: ["id", "entryId", "values.name", "values.price"]
});Querying Entries
List All Entries
import { sdk } from "@/lib/webiny";
import type { Product } from "@/lib/types";
const result = await sdk.cms.listEntries<Product>({
modelId: "product",
fields: ["id", "entryId", "values.name", "values.description", "values.price", "values.sku"]
});
if (result.isOk()) {
const products = result.value.data;
// products is CmsEntryData<Product>[]
}Sorting Results
const result = await sdk.cms.listEntries<Product>({
modelId: "product",
fields: ["id", "entryId", "values.name", "values.price"],
sort: ["values.name_ASC"]
});Sort options:
values.{fieldId}_ASC- Ascending ordervalues.{fieldId}_DESC- Descending ordercreatedOn_ASC/createdOn_DESC- Sort by creation datesavedOn_ASC/savedOn_DESC- Sort by last saved date
Filtering Results
const result = await sdk.cms.listEntries<Product>({
modelId: "product",
fields: ["id", "entryId", "values.name", "values.price"],
where: {
"values.price_gte": 100
}
});Common filter operators:
_eq- Equals_ne- Not equals_in- In array_not_in- Not in array_lt- Less than_lte- Less than or equal_gt- Greater than_gte- Greater than or equal_contains- Contains substring_not_contains- Does not contain substring
Pagination
const result = await sdk.cms.listEntries<Product>({
modelId: "product",
fields: ["id", "entryId", "values.name", "values.price"],
limit: 10,
after: cursor // from previous response
});
if (result.isOk()) {
const { data, meta } = result.value;
const hasMore = meta.hasMoreItems;
const nextCursor = meta.cursor;
}Get Single Entry
const result = await sdk.cms.getEntry<Product>({
modelId: "product",
entryId: "abc123",
fields: ["id", "entryId", "values.name", "values.description", "values.price", "values.sku"]
});
if (result.isOk()) {
const product = result.value;
// product is CmsEntryData<Product>
}Working With Reference Fields
To retrieve data from a referenced entry, include the nested field paths in fields. Reference field values are accessed via values.{referenceField}.values.{nestedField}:
const result = await sdk.cms.getEntry<Product>({
modelId: "product",
entryId: "abc123",
fields: [
"id",
"entryId",
"values.name",
"values.price",
"values.category.id",
"values.category.values.name",
"values.category.values.slug"
]
});
if (result.isOk()) {
const product = result.value;
console.log(`Category: ${product.values.category.values.name}`);
console.log(`Slug: ${product.values.category.values.slug}`);
}Mutating Entries
Creating Entries
const result = await sdk.cms.createEntry<Product>({
modelId: "product",
data: {
values: {
name: "Laptop",
description: "High-performance laptop",
price: 1299,
sku: "LAP-001"
}
},
fields: ["id", "entryId", "createdOn", "values.name", "values.price"]
});
if (result.isOk()) {
const newProduct = result.value;
console.log(`Created product: ${newProduct.id}`);
}Important:
createEntry()uses the Manage API- Entry is created in draft status
- Call
publishEntryRevision()to make it publicly visible
Updating Entry Revisions
const result = await sdk.cms.updateEntryRevision<Product>({
modelId: "product",
revisionId: "69b2b114d26d020002c001d2#0001",
data: {
values: {
price: 1199 // updated price
}
},
fields: ["id", "entryId", "values.name", "values.price"]
});
if (result.isOk()) {
const updatedProduct = result.value;
}Note: updateEntryRevision() only modifies the specified fields. Other fields remain unchanged.
Publishing Entry Revisions
const result = await sdk.cms.publishEntryRevision({
modelId: "product",
revisionId: "69b2b114d26d020002c001d2#0001",
fields: ["id", "entryId", "values.name", "values.price"]
});
if (result.isOk()) {
console.log("Entry published successfully");
}Publishing makes an entry available via the Read API. Unpublished (draft) entries are only accessible via the Manage API.
Deleting Entry Revisions
const result = await sdk.cms.deleteEntryRevision({
modelId: "product",
revisionId: "69b2b114d26d020002c001d2#0001"
});
if (result.isOk()) {
console.log("Entry deleted successfully");
}Error Handling
All SDK methods return a Result type. Always check for errors before accessing the value:
const result = await sdk.cms.getEntry<Product>({
modelId: "product",
entryId: "abc123",
fields: ["id", "entryId", "values.name", "values.price"]
});
if (result.isFail()) {
console.error("Error:", result.error.message);
console.error("Code:", result.error.code);
return;
}
const product = result.value;Common error scenarios:
- Entry not found (
entryIddoes not exist) - Permission denied (API key lacks permissions)
- Validation errors (required fields missing)
- Network errors (API unreachable)