技能 编程开发 Webiny SDK:无头CMS集成与开发

Webiny SDK:无头CMS集成与开发

v20260424
webiny-sdk
这是Webiny官方的SDK,用于将无头内容管理系统(Headless CMS)和文件管理器集成到外部应用(如Next.js、Vue等)。它提供了一个完整的TypeScript接口,支持开发者执行内容的读取、创建、更新、发布等全生命周期操作。支持高级功能,如内容修订版本控制、过滤与排序查询,并确保了类型安全,极大地提高了开发效率。
获取技能
312 次下载
概览

Webiny SDK

TL;DR

The @webiny/sdk package provides a TypeScript interface for external apps (Next.js, Vue, Node.js) to interact with Webiny's Headless CMS and File Manager. Every method returns a Result object (checked with isOk()). Supports listing, getting, creating, updating, publishing, and unpublishing entries with filtering, sorting, pagination, and TypeScript generics for type safety.

Installation & Setup

npm install @webiny/sdk

Initialize once and reuse:

// lib/webiny.ts
import { Webiny } from "@webiny/sdk";

export const webiny = new Webiny({
  token: process.env.WEBINY_API_TOKEN!,
  endpoint: process.env.WEBINY_API_ENDPOINT!,
  tenant: process.env.WEBINY_API_TENANT || "root"
});
  • token -- API key token generated in Webiny Admin (Settings > API Keys)
  • endpoint -- The base API URL, without a trailing slash. Run yarn webiny info in your Webiny project to find the API URL. For Website Builder projects use NEXT_PUBLIC_WEBSITE_BUILDER_API_HOST.
  • tenant -- Tenant ID, defaults to "root"

IMPORTANT: Never add a trailing slash to endpoint. The SDK appends /graphql to the endpoint internally, so https://xxx.cloudfront.net/ will break all requests.

The fields Parameter (Required)

Every SDK method requires a fields array that specifies which fields to return. Omitting it will cause a runtime error.

  • Use "values.<fieldId>" for content fields: "values.name", "values.price"
  • Use top-level field names for metadata: "id", "entryId", "createdOn", "status"
  • Use dot notation for nested fields: "values.author.name"
// Minimal fields -- just IDs
fields: ["id", "entryId"];

// Content fields
fields: ["id", "entryId", "values.name", "values.price", "values.description"];

// Nested reference fields
fields: ["id", "values.title", "values.author.name", "values.author.email"];

CMS: Read vs Preview Mode

webiny.cms.listEntries and webiny.cms.getEntry accept a preview parameter to control which revisions are returned:

preview Returns Use For
false (default) Published entries only Public-facing apps, SSG
true Latest revision (draft or published) Content preview, editorial tools

Write operations (createEntry, updateEntryRevision, etc.) are not affected by preview.

The Result Pattern

Every SDK method returns a Result object -- it never throws:

const result = await webiny.cms.listEntries({
  modelId: "product",
  fields: ["id", "values.name"]
});

if (result.isOk()) {
  console.log(result.value.data); // success -- typed data
} else {
  console.error(result.error.message); // failure -- error info
}

TypeScript Generics

Pass a type parameter for full type safety on values:

import type { CmsEntryData } from "@webiny/sdk";

interface Product {
  name: string;
  price: number;
  sku: string;
  description: string;
  category?: CmsEntryData<ProductCategory>;
}

interface ProductCategory {
  name: string;
  slug: string;
}

const result = await webiny.cms.listEntries<Product>({
  modelId: "product",
  fields: ["id", "entryId", "values.name", "values.price", "values.sku"]
});

if (result.isOk()) {
  // result.value.data is CmsEntryData<Product>[]
  const products = result.value.data;
  // products[0].values.name -- fully typed
}

Reference fields like category are typed as CmsEntryData<T>, which wraps referenced entries with id, entryId, and values.

Reading Data

List Entries

const result = await webiny.cms.listEntries<Product>({
  modelId: "product",
  fields: ["id", "entryId", "values.name", "values.price"],
  sort: { "values.name": "asc" },
  limit: 10
});

List with Filters

const result = await webiny.cms.listEntries<Product>({
  modelId: "product",
  fields: ["id", "entryId", "values.name", "values.price"],
  where: {
    "values.price_gte": 100,
    "values.name_contains": "Pro"
  },
  sort: { "values.price": "desc" }
});

Filter Operators

Operator Description Example
_eq Equals (default) "values.status": "active"
_not Not equals "values.status_not": "archived"
_contains Contains substring "values.name_contains": "Pro"
_startsWith Starts with "values.name_startsWith": "Web"
_gt / _gte Greater than / >= "values.price_gte": 100
_lt / _lte Less than / <= "values.price_lt": 500
_in In array "values.status_in": ["active", "featured"]

Sort Format

Sort is a Record<string, "asc" | "desc"> object:

sort: { "values.name": "asc" }     // alphabetical
sort: { "values.price": "desc" }   // highest price first
sort: { "values.createdOn": "desc" } // newest first

Get Single Entry

Use where with either id (revision ID) or entryId:

// By revision ID
const result = await webiny.cms.getEntry<Product>({
  modelId: "product",
  where: { id: "abc123#0001" },
  fields: ["id", "entryId", "values.name", "values.price"]
});

// By entry ID (gets latest published revision)
const result = await webiny.cms.getEntry<Product>({
  modelId: "product",
  where: { entryId: "abc123" },
  fields: ["id", "entryId", "values.name"]
});

Preview Mode (Drafts)

Pass preview: true to listEntries or getEntry to access unpublished/draft content:

const result = await webiny.cms.listEntries<Product>({
  modelId: "product",
  fields: ["id", "entryId", "values.name"],
  preview: true // returns drafts + published
});

Writing Data

CRITICAL: Content fields MUST be wrapped inside a values key in the data object. Passing fields directly (without values) will result in an empty or malformed entry.

Create an Entry

const result = await webiny.cms.createEntry({
  modelId: "contactSubmission",
  data: {
    values: {
      // ← REQUIRED: wrap all content fields in `values`
      name: "John Doe",
      email: "john@example.com",
      message: "Hello from the contact form!"
    }
  },
  fields: ["id", "entryId"]
});

Update an Entry Revision

The method is updateEntryRevision, not updateEntry. Use revisionId (the full entryId#revisionNumber, e.g. "abc123#0001"):

const result = await webiny.cms.updateEntryRevision({
  modelId: "product",
  revisionId: "abc123#0001", // ← note: revisionId, not id
  data: {
    values: {
      // ← REQUIRED: wrap fields in `values`
      price: 29.99
    }
  },
  fields: ["id", "entryId", "values.price"]
});

Publish / Unpublish

The methods are publishEntryRevision and unpublishEntryRevision, not publishEntry/unpublishEntry. Both require revisionId and fields:

await webiny.cms.publishEntryRevision({
  modelId: "product",
  revisionId: "abc123#0001",
  fields: ["id", "entryId", "status"]
});

await webiny.cms.unpublishEntryRevision({
  modelId: "product",
  revisionId: "abc123#0001",
  fields: ["id", "entryId", "status"]
});

Delete an Entry Revision

await webiny.cms.deleteEntryRevision({
  modelId: "product",
  revisionId: "abc123#0001",
  fields: []
});

Languages

webiny.languages.listLanguages() returns all enabled languages — disabled languages are always filtered out server-side, so no filter parameter is needed.

import type { Language } from "@webiny/sdk";

const result = await webiny.languages.listLanguages();

if (result.isOk()) {
  const languages: Language[] = result.value;
  // languages[0].code, .name, .direction, .isDefault
}

The Language type:

interface Language {
  id: string;
  code: string; // e.g. "en-US"
  name: string; // e.g. "English (US)"
  direction?: "ltr" | "rtl";
  isDefault?: boolean;
}

File Manager

// List files
const files = await webiny.fileManager.listFiles({
  limit: 20,
  fields: ["id", "key", "name", "size", "type", "src"]
});

// Upload a file (returns presigned URL for direct S3 upload)
const uploaded = await webiny.fileManager.uploadFile({ file: myFile });

Creating API Keys via Code

For programmatic access, create API keys as an extension:

// extensions/MyApiKey.ts
import { ApiKeyFactory } from "webiny/api/security";

class MyApiKeyImpl implements ApiKeyFactory.Interface {
  execute(): ApiKeyFactory.Return {
    return [
      {
        name: "Universal API Key",
        slug: "universal-key",
        token: "wat_12345678",
        permissions: [{ name: "*" }]
      }
    ];
  }
}

export default ApiKeyFactory.createImplementation({
  implementation: MyApiKeyImpl,
  dependencies: []
});

Register (YOU MUST include the .ts file extension in the src prop — omitting it will cause a build failure):

<Api.Extension src={"/extensions/MyApiKey.ts"} />

Background Tasks

webiny.tasks wraps the Background Tasks GraphQL API. All methods return a Result and never throw.

List Task Definitions

Returns all registered task definitions — use this to discover valid definition IDs before triggering.

const result = await webiny.tasks.listDefinitions();

if (result.isOk()) {
  // result.value: TaskDefinition[]
  for (const def of result.value) {
    console.log(def.id, def.title, def.description);
  }
}

List Task Runs

const result = await webiny.tasks.listTasks();

if (result.isOk()) {
  // result.value: TaskRun[]
  for (const task of result.value) {
    console.log(task.id, task.taskStatus, task.definitionId);
  }
}

List Task Logs

Optionally filter by a specific task run ID:

// All logs
const result = await webiny.tasks.listLogs();

// Logs for a specific task run
const result = await webiny.tasks.listLogs({
  where: { task: "yourTaskRunId" }
});

if (result.isOk()) {
  for (const log of result.value) {
    for (const item of log.items) {
      console.log(`[${item.type}] ${item.message}`);
    }
  }
}

Trigger a Task

const result = await webiny.tasks.triggerTask({
  definition: "myTaskDefinitionId",
  input: {
    someVariable: "someValue",
    anotherVariable: 42
  }
});

if (result.isOk()) {
  const task = result.value; // TaskRun
  console.log(task.id, task.taskStatus, task.executionName);
}

Abort a Task

The task stops at its next safe checkpoint.

const result = await webiny.tasks.abortTask({
  id: "yourTaskRunId",
  message: "Stopped by user request" // optional
});

if (result.isOk()) {
  console.log(result.value.taskStatus); // "aborted"
}

Background Task Types

import type { TaskDefinition, TaskRun, TaskLog, TaskLogItem, TaskStatus } from "@webiny/sdk";

type TaskStatus = "pending" | "running" | "completed" | "failed" | "aborted" | "stopped";

interface TaskDefinition {
  id: string;
  title: string;
  description?: string;
}

interface TaskRun {
  id: string;
  definitionId: string;
  taskStatus: TaskStatus;
  input?: unknown;
  output?: unknown;
  startedOn?: string;
  finishedOn?: string;
  executionName?: string;
  iterations?: number;
  parentId?: string;
}

SDK Modules Reference

Module Webiny App What You Can Do
webiny.cms Headless CMS List, get, create, update, publish, unpublish, delete entry revisions
webiny.fileManager File Manager List, upload, and manage files and folders
webiny.tenantManager Multi-tenancy Create, install, enable, disable tenants
webiny.languages Languages List enabled languages (id, code, name, direction, isDefault)
webiny.tasks Background Tasks Trigger, abort, list task runs, definitions, and logs

Common Mistakes

Mistake Correct
data: { name: "..." } data: { values: { name: "..." } }
updateEntry(...) updateEntryRevision(...)
publishEntry(...) publishEntryRevision(...)
unpublishEntry(...) unpublishEntryRevision(...)
sort: ["values.name_ASC"] sort: { "values.name": "asc" }
getEntry({ id: "..." }) getEntry({ where: { id: "..." } })
Omitting fields Always provide fields: [...]
Trailing slash in endpoint Remove trailing slash from endpoint URL
triggerTask with unknown definition string Use an ID returned by listDefinitions() — the GQL schema validates it against WebinyBackgroundTaskDefinitionEnum!

Quick Reference

Install:              npm install @webiny/sdk
Import:               import { Webiny } from "@webiny/sdk";
Type import:          import type { CmsEntryData, TaskRun } from "@webiny/sdk";
Initialize:           new Webiny({ token, endpoint, tenant })
Result check:         result.isOk() -> result.value / result.error.message
API endpoint:         yarn webiny info (in your Webiny project) -- NO trailing slash
Preview mode:         pass preview: true to listEntries / getEntry
fields required:      every CMS method needs a fields: string[] array
values wrapper:       createEntry/updateEntryRevision data must use { values: { ... } }
Background tasks:     webiny.tasks.triggerTask({ definition, input })
Abort task:           webiny.tasks.abortTask({ id, message? })
Filter logs by task:  webiny.tasks.listLogs({ where: { task: "id" } })

Related Skills

  • webiny-api-cms-content-models -- Define the models you query with the SDK
  • webiny-website-builder -- Use the SDK inside Website Builder components to fetch CMS data
信息
Category 编程开发
Name webiny-sdk
版本 v20260424
大小 15.87KB
更新时间 2026-04-28
语言