Loading...
Loading...
Write and run Contentful content model migration scripts using the contentful-migration library and the Contentful CLI. Covers creating, editing, and deleting content types and fields, validations, editor interface configuration, editor layouts, sidebar widgets, entry transformations, tags, annotations, and the migration context object. Use when asked to write a migration, create or add a content type, add, rename, or delete fields, change or update a content model, transform entries, derive linked entries, configure editor controls, or run a migration script. Also triggers on "migration script", "contentful-migration", "schema migration", "content model migration", "field validation", "editor interface", "editor layout", "sidebar widget", "moveField", "changeFieldId", "rich text field", "reference field", "link field". Not for SDK client setup or Next.js integration (contentful-nextjs). Not for Contentful terminology or API routing (contentful-guide).
npx skill4agent add contentful/skills contentful-migrationcontentful-migrationnpm install contentful-migrationnpx contentful space migrationnpx contentful-migrationcontentful-clinpx contentful ...https://www.contentful.com/developers/docs/tools/mcp-server/contentful-migrationcontentful-climigrationimport 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 = migrationcontextmakeRequestspaceIdaccessTokenmakeRequestecho "=== 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)".envcontentful environment create --name sandbox --source masterCONTENTFUL_SPACE_ID/spaces/<SPACE_ID>/...CONTENTFUL_MANAGEMENT_ACCESS_TOKENhttps://app.contentful.com/account/profile/cma_tokenshttps://app.contentful.com/spaces/<SPACE_ID>/api/cma_tokensCONTENTFUL_ENVIRONMENT_IDmastersandbox--environment-idconst page = migration.createContentType('page', {
name: 'Page',
description: 'A generic page',
displayField: 'title',
})const page = migration.editContentType('page')
page.description('Updated description')
page.displayField('internalName')migration.deleteContentType('page')transformEntriesToTypepage.createField('title')
.name('Title')
.type('Symbol')
.required(true)
.localized(true)page.editField('title')
.name('Page Title')
.required(false)page.deleteField('legacyField')page.changeFieldId('oldName', 'newName')page.moveField('slug').afterField('title')
page.moveField('featured').toTheTop()
page.moveField('metadata').toTheBottom()
page.moveField('author').beforeField('publishDate')| 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 |
| 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) | |
.validations([...])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'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] }
},
})authorblogPost.authorNameauthorRefconst 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' })builtinextensionapp001-create-blog-post.ts002-add-author-field.ts003-transform-categories.tsshouldPublish: 'preserve'context.makeRequestitemstype: 'Array'itemstransformEntriesToTypelinkTypetype: 'Link'linkType: 'Asset'linkType: 'Entry'--environment-id sandboxtransformEntryForLocaleundefineddisplayFieldSymbol