contentful-migration
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseContentful Migration
Contentful 迁移
The tool lets you describe and execute content model changes as code. Migrations are TypeScript scripts that create, edit, or delete content types, fields, editor interfaces, and entries.
contentful-migrationInstall:
bash
npm install contentful-migrationcontentful-migration安装:
bash
npm install contentful-migrationScope
适用范围
This skill covers:
- Content type and field CRUD operations
- Field types, validations, and editor interface configuration
- Entry transformations (in-place transforms, deriving linked entries, cross-type transforms)
- Tags, annotations, taxonomy validations
- Editor layouts, sidebar widgets
- Running migrations via (Contentful CLI) and programmatic API
npx contentful space migration
Do not run migrations with . Use for CLI execution, install it as a dev dependency when needed, and run via .
npx contentful-migrationcontentful-clinpx contentful ...Not covered: SDK client setup (the contentful-nextjs skill), Contentful concepts and API routing (the contentful-guide skill).
本技能涵盖:
- 内容类型与字段的增删改查(CRUD)操作
- 字段类型、验证规则和编辑器界面配置
- 条目转换(原地转换、派生关联条目、跨类型转换)
- 标签、注释、分类验证
- 编辑器布局、侧边栏小部件
- 通过 (Contentful CLI)和程序化API运行迁移
npx contentful space migration
请勿使用 运行迁移。使用 执行CLI操作,必要时将其安装为开发依赖,并通过 运行。
npx contentful-migrationcontentful-clinpx contentful ...未涵盖内容:SDK客户端设置(对应contentful-nextjs技能)、Contentful概念与API路由(对应contentful-guide技能)。
Contentful MCP note
Contentful MCP 说明
- For users who want easier agent interaction with Contentful while planning or reviewing migrations, point them to the Contentful MCP server docs: .
https://www.contentful.com/developers/docs/tools/mcp-server/ - Continue to use scripts and
contentful-migrationfor actual migration execution.contentful-cli
- 若用户希望在规划或审核迁移时更便捷地与Contentful交互,可引导其查看Contentful MCP服务器文档:。
https://www.contentful.com/developers/docs/tools/mcp-server/ - 实际执行迁移时仍需使用 脚本和
contentful-migration。contentful-cli
Migration Script Format
迁移脚本格式
Every migration file exports a function that receives a object:
migrationtypescript
import type { MigrationFunction } from 'contentful-migration'
const migration: MigrationFunction = (migration) => {
const blogPost = migration.createContentType('blogPost', {
name: 'Blog Post',
description: 'A blog post entry',
displayField: 'title',
})
blogPost.createField('title')
.name('Title')
.type('Symbol')
.required(true)
}
export = migrationThe function also receives a object as its second parameter, providing (direct CMA access), , and . Use when you need data not available through the migration API.
contextmakeRequestspaceIdaccessTokenmakeRequest每个迁移文件都导出一个接收 对象的函数:
migrationtypescript
import type { MigrationFunction } from 'contentful-migration'
const migration: MigrationFunction = (migration) => {
const blogPost = migration.createContentType('blogPost', {
name: 'Blog Post',
description: 'A blog post entry',
displayField: 'title',
})
blogPost.createField('title')
.name('Title')
.type('Symbol')
.required(true)
}
export = migration该函数还会接收第二个参数 对象,提供 (直接访问CMA)、 和 。当需要迁移API未提供的数据时,可使用 。
contextmakeRequestspaceIdaccessTokenmakeRequestProject state
项目状态
echo "=== Existing migrations ===" && ls migrations/ 2>/dev/null || echo "(no migrations/ directory found)"
echo ""
echo "=== Contentful env vars ===" && grep -h CONTENTFUL .env .env.local 2>/dev/null | sed 's/=.*/=<set>/' || echo "(no Contentful env vars found in .env or .env.local)"echo "=== Existing migrations ===" && ls migrations/ 2>/dev/null || echo "(no migrations/ directory found)"
echo ""
echo "=== Contentful env vars ===" && grep -h CONTENTFUL .env .env.local 2>/dev/null | sed 's/=.*/=<set>/' || echo "(no Contentful env vars found in .env or .env.local)"Workflow
工作流程
When writing a migration:
- Confirm required env vars first. If values are missing, ask the user to add them to a local file before proceeding.
.env - Assess the change. Identify which content types and fields need to change. Check the current content model in the Contentful web app or via CMA.
- Write the migration script. Use the operations below. Prefer chaining over object notation — it gives better error messages with line numbers.
- Test in a sandbox environment. Never run untested migrations against production. Create a sandbox environment first: .
contentful environment create --name sandbox --source master - Run the migration. See Running Migrations for CLI and programmatic options.
- Verify. Check the content model in the web app. Confirm entries are intact.
编写迁移脚本时:
- 首先确认所需环境变量。若缺少值,请用户将其添加到本地 文件后再继续。
.env - 评估变更内容。确定需要修改的内容类型和字段。通过Contentful网页应用或CMA查看当前内容模型。
- 编写迁移脚本。使用以下操作方法。优先使用链式调用而非对象表示法——链式调用能提供带行号的更清晰错误信息。
- 在沙箱环境中测试。切勿在生产环境中运行未测试的迁移。先创建沙箱环境:。
contentful environment create --name sandbox --source master - 运行迁移。查看运行迁移了解CLI和程序化选项。
- 验证结果。在网页应用中检查内容模型,确认条目完整无缺。
Required environment variables
所需环境变量
- - Space ID. Find it in the Contentful web app URL (
CONTENTFUL_SPACE_ID) or in Space settings -> API keys./spaces/<SPACE_ID>/... - - CMA token used for migrations. Create it in Account settings -> CMA tokens (
CONTENTFUL_MANAGEMENT_ACCESS_TOKEN) or from a space-scoped CMA tokens page (https://app.contentful.com/account/profile/cma_tokens).https://app.contentful.com/spaces/<SPACE_ID>/api/cma_tokens - (optional) - Target environment ID (for example
CONTENTFUL_ENVIRONMENT_IDormaster) when you want to avoid passingsandbox.--environment-id
If any required value is missing, explicitly ask the user for the missing values and tell them where to find each one.
- - 空间ID。可在Contentful网页应用URL(
CONTENTFUL_SPACE_ID)或空间设置 -> API密钥中找到。/spaces/<SPACE_ID>/... - - 用于迁移的CMA令牌。可在账户设置 -> CMA令牌(
CONTENTFUL_MANAGEMENT_ACCESS_TOKEN)或空间范围的CMA令牌页面(https://app.contentful.com/account/profile/cma_tokens)中创建。https://app.contentful.com/spaces/<SPACE_ID>/api/cma_tokens - (可选)- 目标环境ID(例如
CONTENTFUL_ENVIRONMENT_ID或master),设置后无需传递sandbox参数。--environment-id
若缺少任何必填值,请明确向用户索要缺失值,并告知每个值的获取位置。
Content Type Operations
内容类型操作
Create a content type:
typescript
const page = migration.createContentType('page', {
name: 'Page',
description: 'A generic page',
displayField: 'title',
})Edit an existing content type:
typescript
const page = migration.editContentType('page')
page.description('Updated description')
page.displayField('internalName')Delete a content type:
typescript
migration.deleteContentType('page')Content type must have zero entries before deletion. Delete all entries first, or use to move them.
transformEntriesToType创建内容类型:
typescript
const page = migration.createContentType('page', {
name: 'Page',
description: 'A generic page',
displayField: 'title',
})编辑现有内容类型:
typescript
const page = migration.editContentType('page')
page.description('Updated description')
page.displayField('internalName')删除内容类型:
typescript
migration.deleteContentType('page')删除内容类型前必须确保其下无条目。需先删除所有条目,或使用 转移条目。
transformEntriesToTypeField Operations
字段操作
Create a field:
typescript
page.createField('title')
.name('Title')
.type('Symbol')
.required(true)
.localized(true)Edit an existing field:
typescript
page.editField('title')
.name('Page Title')
.required(false)Delete a field:
typescript
page.deleteField('legacyField')Deleting a field permanently removes its content from all entries.
Change a field ID:
typescript
page.changeFieldId('oldName', 'newName')Existing content is preserved — only the ID changes.
Move a field:
typescript
page.moveField('slug').afterField('title')
page.moveField('featured').toTheTop()
page.moveField('metadata').toTheBottom()
page.moveField('author').beforeField('publishDate')创建字段:
typescript
page.createField('title')
.name('Title')
.type('Symbol')
.required(true)
.localized(true)编辑现有字段:
typescript
page.editField('title')
.name('Page Title')
.required(false)删除字段:
typescript
page.deleteField('legacyField')删除字段会永久移除所有条目中该字段的内容。
修改字段ID:
typescript
page.changeFieldId('oldName', 'newName')现有内容会被保留——仅字段ID变更。
移动字段位置:
typescript
page.moveField('slug').afterField('title')
page.moveField('featured').toTheTop()
page.moveField('metadata').toTheBottom()
page.moveField('author').beforeField('publishDate')Field Types Quick Reference
字段类型速查
| Type | Description | Extra config |
|---|---|---|
| Short text (max 256 chars) | — |
| Long text (max 50,000 chars) | — |
| Whole number | — |
| Decimal number | — |
| ISO 8601 date/time | — |
| True/false | — |
| Arbitrary JSON | — |
| Lat/lon coordinates | — |
| Structured rich text | |
| List of values or references | Requires |
| Single reference | Requires |
| Cross-space reference | Requires |
See API Reference — Field Types for full configuration details.
| 类型 | 描述 | 额外配置 |
|---|---|---|
| 短文本(最多256字符) | — |
| 长文本(最多50,000字符) | — |
| 整数 | — |
| 小数 | — |
| ISO 8601 日期/时间 | — |
| 布尔值(是/否) | — |
| 任意JSON数据 | — |
| 经纬度坐标 | — |
| 结构化富文本 | |
| 值或引用的列表 | 需指定 |
| 单个引用 | 需指定 |
| 跨空间引用 | 需指定 |
查看API参考——字段类型获取完整配置详情。
Validations Quick Reference
验证规则速查
| Validation | Applies to | Example |
|---|---|---|
| Symbol, Integer, Number | |
| Symbol, Integer, Number | |
| Array, Text, Symbol | |
| Integer, Number | |
| Symbol, Text | |
| Date | |
| Link, Array of Links | |
| Link (Asset) | |
| Link (Asset) | |
| Link (Asset) | |
Apply validations via on a field. See API Reference — Validations for all options.
.validations([...])| 验证规则 | 适用字段类型 | 示例 |
|---|---|---|
| Symbol、Integer、Number | |
| Symbol、Integer、Number | |
| Array、Text、Symbol | |
| Integer、Number | |
| Symbol、Text | |
| Date | |
| Link、Link数组 | |
| Link(Asset) | |
| Link(Asset) | |
| Link(Asset) | |
通过字段的 方法应用验证规则。查看API参考——验证规则获取所有选项。
.validations([...])Entry Transformations
条目转换
Transform entries in place:
typescript
migration.transformEntries({
contentType: 'blogPost',
from: ['firstName', 'lastName'],
to: ['fullName'],
transformEntryForLocale: (fields, locale) => {
const first = fields.firstName[locale]
const last = fields.lastName[locale]
if (!first && !last) return
return { fullName: `${first || ''} ${last || ''}`.trim() }
},
})Options: (, , or — default ).
shouldPublishtruefalse'preserve''preserve'Derive linked entries:
typescript
migration.deriveLinkedEntries({
contentType: 'blogPost',
derivedContentType: 'author',
from: ['authorName'],
toReferenceField: 'authorRef',
derivedFields: ['name'],
identityKey: (fields) =>
fields.authorName['en-US'].toLowerCase().replace(/\s+/g, '-'),
deriveEntryForLocale: (fields, locale) => {
if (locale !== 'en-US') return
return { name: fields.authorName[locale] }
},
})This creates new entries from existing data and links them via .
authorblogPost.authorNameauthorRefSee Patterns — Transform Entries and Patterns — Derive Linked Entries for more examples.
原地转换条目:
typescript
migration.transformEntries({
contentType: 'blogPost',
from: ['firstName', 'lastName'],
to: ['fullName'],
transformEntryForLocale: (fields, locale) => {
const first = fields.firstName[locale]
const last = fields.lastName[locale]
if (!first && !last) return
return { fullName: `${first || ''} ${last || ''}`.trim() }
},
})可选参数:(、 或 ——默认值为 )。
shouldPublishtruefalse'preserve''preserve'派生关联条目:
typescript
migration.deriveLinkedEntries({
contentType: 'blogPost',
derivedContentType: 'author',
from: ['authorName'],
toReferenceField: 'authorRef',
derivedFields: ['name'],
identityKey: (fields) =>
fields.authorName['en-US'].toLowerCase().replace(/\s+/g, '-'),
deriveEntryForLocale: (fields, locale) => {
if (locale !== 'en-US') return
return { name: fields.authorName[locale] }
},
})此操作会从现有 数据创建新的 条目,并通过 建立关联。
blogPost.authorNameauthorauthorRef查看模式——转换条目和模式——派生关联条目获取更多示例。
Editor Interface
编辑器界面
Change the widget for a field:
typescript
const page = migration.editContentType('page')
page.changeFieldControl('slug', 'builtin', 'slugEditor', {
helpText: 'URL-friendly identifier',
trackingFieldId: 'title',
})
page.changeFieldControl('category', 'builtin', 'dropdown')
page.changeFieldControl('publishDate', 'builtin', 'datePicker', { format: 'dateonly' })Widget namespaces: , (UI extensions), (custom apps).
builtinextensionappSee API Reference — Editor Interface for all built-in widgets and their settings.
修改字段的小部件:
typescript
const page = migration.editContentType('page')
page.changeFieldControl('slug', 'builtin', 'slugEditor', {
helpText: 'URL-friendly identifier',
trackingFieldId: 'title',
})
page.changeFieldControl('category', 'builtin', 'dropdown')
page.changeFieldControl('publishDate', 'builtin', 'datePicker', { format: 'dateonly' })小部件命名空间:(内置)、(UI扩展)、(自定义应用)。
builtinextensionapp查看API参考——编辑器界面获取所有内置小部件及其设置。
Best Practices
最佳实践
- Number migration files sequentially: ,
001-create-blog-post.ts,002-add-author-field.ts.003-transform-categories.ts - One logical change per migration. Easier to debug, revert, and review.
- Always test in a sandbox environment before running against production.
- Use (the default) to maintain existing publish states during transforms.
shouldPublish: 'preserve' - Prefer chaining over object notation — chaining gives line-level error messages.
- Split data transforms from schema changes. First migration changes the schema, second transforms data. This makes each step independently verifiable.
- Use sparingly — only when the migration API doesn't cover your use case.
context.makeRequest
- 按顺序为迁移文件编号:、
001-create-blog-post.ts、002-add-author-field.ts。003-transform-categories.ts - 每个迁移仅包含一个逻辑变更。便于调试、回滚和审核。
- 始终在沙箱环境中测试,再在生产环境中运行。
- 使用 (默认值)以在转换过程中保留现有发布状态。
shouldPublish: 'preserve' - 优先使用链式调用而非对象表示法——链式调用能提供行级错误信息。
- 将数据转换与架构变更分离。第一个迁移修改架构,第二个迁移转换数据。这样每个步骤都可独立验证。
- 谨慎使用 ——仅当迁移API无法满足需求时使用。
context.makeRequest
Common Mistakes
常见错误
- Forgetting on Array fields.
itemsrequires antype: 'Array'property specifying the element type.items - Deleting a content type with entries. You must delete all entries first, or move them with .
transformEntriesToType - Missing on Link fields.
linkTyperequirestype: 'Link'orlinkType: 'Asset'.linkType: 'Entry' - Running against master. Always test in a sandbox environment. Use on the CLI.
--environment-id sandbox - Not handling missing locales in transforms. is called for every locale — return
transformEntryForLocaleto skip.undefined - Setting to a non-Symbol field. The display field must be of type
displayField.Symbol
- Array字段忘记设置 。
items需要指定type: 'Array'属性来定义元素类型。items - 删除包含条目的内容类型。必须先删除所有条目,或使用 转移条目。
transformEntriesToType - Link字段缺少 。
linkType需要指定type: 'Link'或linkType: 'Asset'。linkType: 'Entry' - 直接在master环境运行。始终在沙箱环境测试。在CLI中使用 参数。
--environment-id sandbox - 转换时未处理缺失的语言环境。会针对每个语言环境调用——返回
transformEntryForLocale可跳过该语言环境。undefined - 将displayField设置为非Symbol类型字段。显示字段必须为Symbol类型。
References
参考资料
- API Reference — complete migration API surface
- Patterns — common migration examples
- Running Migrations — CLI, programmatic API, CI/CD
- API参考——完整的迁移API列表
- 模式——常见迁移示例
- 运行迁移——CLI、程序化API、CI/CD
Related Skills
相关技能
- the contentful-guide skill — Contentful concepts, terminology, API routing
- the contentful-nextjs skill — Next.js integration with Contentful
- contentful-guide技能——Contentful概念、术语、API路由
- contentful-nextjs技能——Contentful与Next.js集成