This project is not covered by Drupal’s security advisory policy.

Contentful Migration moves a Contentful space export into Drupal using the core Migrate API, handling the parts a YAML-only migration can't: Rich Text, embedded entries and assets, media, and cross-reference links.

It is a runtime, not a fixed content model. Every Contentful space is modelled differently, so the module executes per-space migration YAML rather than shipping one schema. The worked migrations in migrations/examples/ are templates you adapt to your space. (The same mapping YAML can optionally be generated by companion content-model analysis tooling.)

What it does

  • Rich Text AST → HTML with embedded entry/asset resolution and inline hyperlink rewriting. The contentful/rich-text library's default renderers emit Contentful-id placeholders (visible garbage) or silently drop unknown nodes. This module owns the full renderer list, resolves sys.id references to the already-migrated Drupal entities, and emits clean <drupal-entity-embed> / <drupal-media> tokens — ending in a logging catch-all that never silently empties. Inline entry-hyperlinks become links to the target entity's canonical page (carrying data-entity-uuid for Linkit-style alias-safe rewriting); asset-hyperlinks link to the migrated file's URL when the media + file modules are installed (plain-text degrade otherwise — nothing silently lost). The bundled recipes/contentful_embed/ ships the text format that renders the tokens: embedded assets on core media's media_embed, embedded-entry tokens via contrib Entity Embed (tag pre-allowed; two-step documented).
  • Assets → Media, staging files into Media entities and de-duplicating identical bytes by SHA-256 (self-contained — no migrate_file_to_media).
  • Reference fields → internal links, rewriting Contentful entry/asset reference fields to alias-safe entity: link URIs.
  • Locale flattening and a two-pass migration (entities first, bodies second) so embeds resolve against already-migrated content.
  • Multi-value Paragraph references via the idiomatic core #2890844 workaround.
  • Authorship timestampssys.createdAt/updatedAt map to created/changed with two core process plugins (worked example included).
  • Repeatable / delta imports — re-export then re-import updates changed entries in place (track_changes, kernel-verified). Deletions don't propagate (a snapshot export has no tombstones) — documented honestly; this is not a sync engine.
  • A drush contentful:export command wraps the contentful-export CLI (assets included) to stage a space's JSON + asset binaries ready for migration; the management token is read from the CONTENTFUL_MANAGEMENT_TOKEN environment variable, never the process argv.

Migrated content lands in standard Drupal entities (nodes, Paragraphs, Media, menus). All three presentation modes are documented with worked reference templates in modes/examples/: decoupled (the JSON:API enable step and the embed-token contract for SPA consumers), recoupled (an editorInterfaces → Drupal widget map — grounded in 208/211 profiled exports — plus four worked display templates), and semi-decoupled (an honest pointer). One drush recipe command makes embeds render; per-space display config is derived from your export, never shipped.

Project status

Early development (1.0.x, beta). The migration runtime — including Rich Text embed and inline-hyperlink resolution — and the contentful:export Drush command are built and covered by unit and kernel tests, including real two-pass Migrate runs that resolve embeds and hyperlinks, stage assets to Media, attach translations, and render a migrated body through the shipped contentful_embed recipe. Both former roadmap items shipped in 1.0.0-beta1 (presentation-mode templates + embed recipe; asset-hyperlinks to the migrated file). Still on the roadmap: opt-in author → user mapping.

This project is not yet covered by Drupal's security advisory policy.

Requirements

  • Drupal 11
  • PHP 8.3+
  • Core Migrate (the only hard dependency)
  • contentful/rich-text ^4.0 (pulled in via Composer)

Paragraphs, Media, and migrate_plus are needed only when your mapping uses them — see the README.

Getting started

composer require 'drupal/contentful_migration:^1.0@beta'
drush en contentful_migration

Then stage your space and adapt the example migrations:

CONTENTFUL_MANAGEMENT_TOKEN=cfpat-… drush contentful:export --space-id=YOUR_SPACE
# edit migrations/examples/ to match your content model

Not in scope

Each exclusion is a deliberate decision, with the evidence it rests on (211 real space exports profiled):

  • Live/bidirectional sync. One-way migration only; repeatable delta imports cover the re-export case — a live two-way bridge is a different product.
  • Full edit/revision history. Exports carry only version counters — none of the 211 profiled exports contain revision snapshots (recovering history would need per-entry Management-API calls). Authorship metadata does migrate: timestamps now, author → user mapping on the roadmap.
  • Roles/permissions. Present in most exports (154/211), but Contentful's policy rules don't map onto Drupal's permission model — auto-generating roles risks granting more than intended. Model roles deliberately in Drupal.
  • Webhooks, UI extensions, SSO. Contentful platform config with no safe Drupal equivalent.
  • Theme/design-system generation. Recoupled presentation consumes a provided theme.
Supporting organizations: 
Orignial Developers

Project information

Releases