contentful-migration

작성자: contentful

Contentful 콘텐츠 모델 마이그레이션 스크립트를 작성하고 실행하는 방법으로, contentful-migration 라이브러리와 Contentful CLI를 사용합니다. 생성, 편집, 삭제 등을 다룹니다.

npx skills add https://github.com/contentful/skills --skill contentful-migration

Contentful Migration

The contentful-migration 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.

Install:

npm install contentful-migration

GitHub: https://github.com/contentful/contentful-migration

Scope

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 npx contentful space migration (Contentful CLI) and programmatic API

Do not run migrations with npx contentful-migration. Use contentful-cli for CLI execution, install it as a dev dependency when needed, and run via npx contentful ....

Not covered: SDK client setup (the contentful-nextjs skill), Contentful concepts and API routing (the contentful-guide skill).

Contentful MCP note

  • 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 contentful-migration scripts and contentful-cli for actual migration execution.

Migration Script Format

Every migration file exports a function that receives a migration object:

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

The function also receives a context object as its second parameter, providing makeRequest (direct CMA access), spaceId, and accessToken. Use makeRequest when you need data not available through the migration API.

Project 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)"

Workflow

When writing a migration:

  1. Confirm required env vars first. If values are missing, ask the user to add them to a local .env file before proceeding.
  2. 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.
  3. Write the migration script. Use the operations below. Prefer chaining over object notation — it gives better error messages with line numbers.
  4. Test in a sandbox environment. Never run untested migrations against production. Create a sandbox environment first: contentful environment create --name sandbox --source master.
  5. Run the migration. See Running Migrations for CLI and programmatic options.
  6. Verify. Check the content model in the web app. Confirm entries are intact.

Required environment variables

  • CONTENTFUL_SPACE_ID - Space ID. Find it in the Contentful web app URL (/spaces/<SPACE_ID>/...) or in Space settings -> API keys.
  • CONTENTFUL_MANAGEMENT_ACCESS_TOKEN - CMA token used for migrations. Create it in Account settings -> CMA tokens (https://app.contentful.com/account/profile/cma_tokens) or from a space-scoped CMA tokens page (https://app.contentful.com/spaces/<SPACE_ID>/api/cma_tokens).
  • CONTENTFUL_ENVIRONMENT_ID (optional) - Target environment ID (for example master or sandbox) when you want to avoid passing --environment-id.

If any required value is missing, explicitly ask the user for the missing values and tell them where to find each one.

Content Type Operations

Create a content type:

const page = migration.createContentType('page', {
  name: 'Page',
  description: 'A generic page',
  displayField: 'title',
})

Edit an existing content type:

const page = migration.editContentType('page')
page.description('Updated description')
page.displayField('internalName')

Delete a content type:

migration.deleteContentType('page')

Content type must have zero entries before deletion. Delete all entries first, or use transformEntriesToType to move them.

Field Operations

Create a field:

page.createField('title')
  .name('Title')
  .type('Symbol')
  .required(true)
  .localized(true)

Edit an existing field:

page.editField('title')
  .name('Page Title')
  .required(false)

Delete a field:

page.deleteField('legacyField')

Deleting a field permanently removes its content from all entries.

Change a field ID:

page.changeFieldId('oldName', 'newName')

Existing content is preserved — only the ID changes.

Move a field:

page.moveField('slug').afterField('title')
page.moveField('featured').toTheTop()
page.moveField('metadata').toTheBottom()
page.moveField('author').beforeField('publishDate')

Field Types Quick Reference

TypeDescriptionExtra config
SymbolShort text (max 256 chars)
TextLong text (max 50,000 chars)
IntegerWhole number
NumberDecimal number
DateISO 8601 date/time
BooleanTrue/false
ObjectArbitrary JSON
LocationLat/lon coordinates
RichTextStructured rich textenabledNodeTypes, enabledMarks validations
ArrayList of values or referencesRequires items: { type, linkType?, validations? }
LinkSingle referenceRequires linkType: 'Asset' or 'Entry'
ResourceLinkCross-space referenceRequires allowedResources

See API Reference — Field Types for full configuration details.

Validations Quick Reference

ValidationApplies toExample
inSymbol, Integer, Number{ in: ['draft', 'published', 'archived'] }
uniqueSymbol, Integer, Number{ unique: true }
sizeArray, Text, Symbol{ size: { min: 1, max: 5 } }
rangeInteger, Number{ range: { min: 0, max: 100 } }
regexpSymbol, Text{ regexp: { pattern: '^[a-z0-9-]+$' } }
dateRangeDate{ dateRange: { min: '2020-01-01', max: '2030-12-31' } }
linkContentTypeLink, Array of Links{ linkContentType: ['author', 'organization'] }
linkMimetypeGroupLink (Asset){ linkMimetypeGroup: ['image', 'video'] }
assetFileSizeLink (Asset){ assetFileSize: { min: 0, max: 5242880 } }
assetImageDimensionsLink (Asset){ assetImageDimensions: { width: { min: 100, max: 2000 } } }

Apply validations via .validations([...]) on a field. See API Reference — Validations for all options.

Entry Transformations

Transform entries in place:

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: shouldPublish (true, false, or 'preserve' — default 'preserve').

Derive linked entries:

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 author entries from existing blogPost.authorName data and links them via authorRef.

See Patterns — Transform Entries and Patterns — Derive Linked Entries for more examples.

Editor Interface

Change the widget for a field:

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: builtin, extension (UI extensions), app (custom apps).

See API Reference — Editor Interface for all built-in widgets and their settings.

Best Practices

  1. Number migration files sequentially: 001-create-blog-post.ts, 002-add-author-field.ts, 003-transform-categories.ts.
  2. One logical change per migration. Easier to debug, revert, and review.
  3. Always test in a sandbox environment before running against production.
  4. Use shouldPublish: 'preserve' (the default) to maintain existing publish states during transforms.
  5. Prefer chaining over object notation — chaining gives line-level error messages.
  6. Split data transforms from schema changes. First migration changes the schema, second transforms data. This makes each step independently verifiable.
  7. Use context.makeRequest sparingly — only when the migration API doesn't cover your use case.

Common Mistakes

  • Forgetting items on Array fields. type: 'Array' requires an items property specifying the element type.
  • Deleting a content type with entries. You must delete all entries first, or move them with transformEntriesToType.
  • Missing linkType on Link fields. type: 'Link' requires linkType: 'Asset' or linkType: 'Entry'.
  • Running against master. Always test in a sandbox environment. Use --environment-id sandbox on the CLI.
  • Not handling missing locales in transforms. transformEntryForLocale is called for every locale — return undefined to skip.
  • Setting displayField to a non-Symbol field. The display field must be of type Symbol.

References

Related Skills

  • the contentful-guide skill — Contentful concepts, terminology, API routing
  • the contentful-nextjs skill — Next.js integration with Contentful

contentful의 다른 스킬

contentful-custom-app-enhancement
contentful
기존 Contentful App Framework 커스텀 앱을 고객의 저장소에서 개선, 디버깅 및 확장합니다. 사용자가 버그 리포트, 기능 요청 등을 제공할 때 사용하세요.
official
contentful-custom-app-from-scratch
contentful
고객의 자체 리포지토리나 워크스페이스에 새로운 Contentful App Framework 커스텀 앱을 설계, 스캐폴딩, 구축 및 검증합니다. 사용자가 다음을 생성하려는 경우 사용하세요…
official
contentful-help
contentful
Contentful 주제를 진단, 구성 및 조회합니다. 트리거 키워드: contentful help, contentful doctor, contentful setup
official
game-jam
contentful
브라우저 기반 테트리스 게임을 설계, 계획, 구축하는 과정을 안내하는 가이드 게임 제작 스킬입니다. 모든 SDK 기본 요소(askUser 등)를 시연합니다.
official
get-to-know-you
contentful
사용자를 알아가는 장난기 가득한 인터뷰로, 프로필 트레이딩 카드를 생성합니다. 사용자가 자신을 소개하고 싶을 때 또는 분위기를 전환하고 싶을 때 사용하세요.
official
contentful-api
contentful
포괄적인 Contentful REST API 가이드. 게시된 콘텐츠를 가져오기 위한 Content Management API(CMA) 및 Content Delivery API(CDA)를 다룹니다.
official
contentful-guide
contentful
Contentful의 핵심 개념을 설명하고 사용자를 적절한 구현 스킬이나 문서로 안내합니다. 사용자가 개념적 질문을 하거나 용어가 필요할 때 사용하세요.
official
contentful-nextjs
contentful
기존 Next.js 프로젝트에 Contentful을 추가하고 구성합니다. JavaScript SDK 설치, 환경 변수 구성, 프로덕션 및… 생성을 다룹니다.
official