contentful-migration について
contentful-migrationは、Contentfulのコンテンツモデルやエントリーの変更をTypeScriptのマイグレーションとして記述するスキルです。Contentful CLIまたはプログラムAPIを使い、開発者がスキーマ更新やデータ変換を計画、テスト、実行できます。
主な機能
- 01コンテンツタイプの作成、編集、削除
- 02フィールド、検証、編集画面の設定
- 03エントリー変換とリンクエントリーの生成
- 04タグ、注釈、タクソノミー検証の管理
- 05Contentful CLIまたはAPIでの実行
活用シーン
- 01コンテンツモデルのフィールド追加や変更
- 02スキーマ更新時の既存エントリー変換
- 03既存コンテンツからリンクエントリーを作成
- 04サンドボックス環境でのマイグレーションテスト
MCP 対応
このスキルは、エージェントによる計画やレビュー向けにContentful MCPサーバーを案内します。マイグレーションスクリプトの実行には引き続きcontentful-cliまたはプログラムAPIを使います。
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ contentful/ skills --skill contentful-migrationインストール後、Claude Code で/contentful-migrationを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
package.jsonreferences/api-reference.mdpatterns.mdrunning-migrations.md詳細
Contentful の移行
contentful-migration ツールを使うと、コンテンツモデルの変更をコードとして記述し、実行できます。マイグレーションは、コンテンツタイプ、フィールド、エディターインターフェース、エントリーを作成、編集、削除する TypeScript スクリプトです。
インストール:
npm install contentful-migration
GitHub:https://github.com/contentful/contentful-migration
対象範囲
このスキルでは、以下を扱います。
- コンテンツタイプとフィールドの CRUD 操作
- フィールドタイプ、検証、エディターインターフェースの設定
- エントリーの変換(インプレース変換、リンクされたエントリーの生成、タイプ間の変換)
- タグ、注釈、分類の検証
- エディターレイアウト、サイドバーウィジェット
npx contentful space migration(Contentful CLI)とプログラムによる API を使ったマイグレーションの実行
npx contentful-migration でマイグレーションを実行しないでください。CLI での実行には contentful-cli を使用し、必要に応じて開発用依存関係としてインストールしてから、npx contentful ... で実行してください。
対象外:SDK クライアントのセットアップ(contentful-nextjs スキル)、Contentful の概念と API ルーティング(contentful-guide スキル)。
Contentful MCP に関する注記
- マイグレーションの計画やレビューにおいて、エージェントと Contentful のやり取りをより簡単にしたいユーザーには、Contentful MCP サーバーのドキュメントを案内してください。
https://www.contentful.com/developers/docs/tools/mcp-server/ - 実際のマイグレーション実行には、引き続き
contentful-migrationスクリプトとcontentful-cliを使用してください。
マイグレーションスクリプトの形式
すべてのマイグレーションファイルは、migration オブジェクトを受け取る関数をエクスポートします。
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
この関数は、2 番目のパラメーターとして context オブジェクトも受け取ります。このオブジェクトには、makeRequest(CMA への直接アクセス)、spaceId、accessToken が含まれます。マイグレーション API では取得できないデータが必要な場合は、makeRequest を使用してください。
プロジェクトの状態
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)"
ワークフロー
マイグレーションを作成する際は、以下の手順に従ってください。
- まず必要な環境変数を確認します。 値が不足している場合は、先にユーザーにローカルの
.envファイルへ追加するよう依頼してください。 - 変更内容を評価します。 変更が必要なコンテンツタイプとフィールドを特定します。Contentful のウェブアプリまたは CMA を使って、現在のコンテンツモデルを確認してください。
- マイグレーションスクリプトを作成します。 以下の操作を使用してください。行番号付きのより適切なエラーメッセージが表示されるため、オブジェクト表記よりもチェーンを優先してください。
- サンドボックス環境でテストします。 テストしていないマイグレーションを本番環境に対して実行しないでください。まずサンドボックス環境を作成します。
contentful environment create --name sandbox --source master - マイグレーションを実行します。 CLI とプログラムによる実行方法については、マイグレーションの実行を参照してください。
- 確認します。 ウェブアプリでコンテンツモデルを確認し、エントリーが損なわれていないことを確認してください。
必要な環境変数
CONTENTFUL_SPACE_ID- Space ID。Contentful のウェブアプリの URL(/spaces/<SPACE_ID>/...)または Space settings -> API keys で確認できます。CONTENTFUL_MANAGEMENT_ACCESS_TOKEN- マイグレーションに使用する CMA トークン。Account settings -> CMA tokens(https://app.contentful.com/account/profile/cma_tokens)またはスペースに限定された CMA トークンのページ(https://app.contentful.com/spaces/<SPACE_ID>/api/cma_tokens)で作成してください。CONTENTFUL_ENVIRONMENT_ID(任意)---environment-idの指定を省略したい場合に使う対象環境の ID(例:masterまたはsandbox)。
必要な値が不足している場合は、不足している値を明示してユーザーに尋ね、それぞれの確認場所を伝えてください。
コンテンツタイプの操作
コンテンツタイプを作成する:
const 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')
削除するコンテンツタイプにはエントリーが 1 つもあってはなりません。すべてのエントリーを先に削除するか、transformEntriesToType を使って移動してください。
フィールドの操作
フィールドを作成する:
page.createField('title')
.name('Title')
.type('Symbol')
.required(true)
.localized(true)
既存のフィールドを編集する:
page.editField('title')
.name('Page Title')
.required(false)
フィールドを削除する:
page.deleteField('legacyField')
フィールドを削除すると、すべてのエントリーからそのコンテンツが完全に削除されます。
フィールド ID を変更する:
page.changeFieldId('oldName', 'newName')
既存のコンテンツは保持され、ID のみが変更されます。
フィールドを移動する:
page.moveField('slug').afterField('title')
page.moveField('featured').toTheTop()
page.moveField('metadata').toTheBottom()
page.moveField('author').beforeField('publishDate')
フィールドタイプのクイックリファレンス
| タイプ | 説明 | 追加設定 |
|---|---|---|
Symbol | 短いテキスト(最大256文字) | — |
Text | 長いテキスト(最大50,000文字) | — |
Integer | 整数 | — |
Number | 小数 | — |
Date | ISO 8601 の日付/時刻 | — |
Boolean | 真/偽 | — |
Object | 任意の JSON | — |
Location | 緯度/経度の座標 | — |
RichText | 構造化されたリッチテキスト | enabledNodeTypes、enabledMarks のバリデーション |
Array | 値または参照のリスト | items が必要です:{ type, linkType?, validations? } |
Link | 単一の参照 | linkType が必要です:'Asset' または 'Entry' |
ResourceLink | スペース間の参照 | allowedResources が必要です |
設定の詳細は、API リファレンス — フィールドタイプをご覧ください。
バリデーションのクイックリファレンス
| バリデーション | 適用対象 | 例 |
|---|---|---|
in | Symbol、Integer、Number | { in: ['draft', 'published', 'archived'] } |
unique | Symbol、Integer、Number | { unique: true } |
size | Array、Text、Symbol | { size: { min: 1, max: 5 } } |
range | Integer、Number | { range: { min: 0, max: 100 } } |
regexp | Symbol、Text | { regexp: { pattern: '^[a-z0-9-]+$' } } |
dateRange | Date | { dateRange: { min: '2020-01-01', max: '2030-12-31' } } |
linkContentType | Link、Link の Array | { linkContentType: ['author', 'organization'] } |
linkMimetypeGroup | Link(Asset) | { linkMimetypeGroup: ['image', 'video'] } |
assetFileSize | Link(Asset) | { assetFileSize: { min: 0, max: 5242880 } } |
assetImageDimensions | Link(Asset) | { assetImageDimensions: { width: { min: 100, max: 2000 } } |
フィールドに .validations([...]) を指定してバリデーションを適用します。すべてのオプションについては、API リファレンス — バリデーションをご覧ください。
エントリーの変換
エントリーをその場で変換します:
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() }
},
})
オプション:shouldPublish(true、false、または '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] }
},
})
これにより、既存の blogPost.authorName データから新しい author エントリーを作成し、authorRef を介してリンクします。
その他の例については、パターン — エントリーの変換とパターン — リンクされたエントリーの派生をご覧ください。
エディターインターフェース
フィールドのウィジェットを変更します:
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' })
ウィジェットの名前空間:builtin、extension(UI 拡張機能)、app(カスタムアプリ)。
組み込みウィジェットとその設定については、API リファレンス — エディターインターフェースをご覧ください。
ベストプラクティス
- マイグレーションファイルには連番を付けます:
001-create-blog-post.ts、002-add-author-field.ts、003-transform-categories.ts。 - 1つのマイグレーションにつき、論理的な変更は1つにします。 デバッグ、取り消し、レビューが容易になります。
- 本番環境で実行する前に、必ずサンドボックス環境でテストします。
- 変換中に既存の公開状態を維持するには、
shouldPublish: 'preserve'(デフォルト)を使用します。 - オブジェクト形式よりもチェーンを優先します — チェーンを使うと、行単位のエラーメッセージが表示されます。
- データ変換とスキーマ変更を分けます。 最初のマイグレーションでスキーマを変更し、2つ目でデータを変換します。これにより、各手順を個別に検証できます。
context.makeRequestの使用は必要最小限にします — マイグレーション API でユースケースに対応できない場合にのみ使用してください。
よくある間違い
- 配列フィールドに
itemsを設定し忘れる。type: 'Array'には、要素の型を指定するitemsプロパティが必要です。 - エントリーがあるコンテンツタイプを削除する。 先にすべてのエントリーを削除するか、
transformEntriesToTypeを使って移動する必要があります。 - リンクフィールドに
linkTypeがない。type: 'Link'には、linkType: 'Asset'またはlinkType: 'Entry'が必要です。 - masterに対して実行する。 必ずサンドボックス環境でテストしてください。CLIでは
--environment-id sandboxを使用します。 - 変換時にロケールの欠落に対応しない。
transformEntryForLocaleはすべてのロケールで呼び出されます。スキップするにはundefinedを返します。 displayFieldにSymbol以外のフィールドを設定する。 表示フィールドの型はSymbolである必要があります。
参考資料
関連スキル
- contentful-guideスキル — Contentfulの概念、用語、APIルーティング
- contentful-nextjsスキル — ContentfulとのNext.js連携