Migrations
The Migrate API imports data into Drupal entities from almost any source, a CSV, a database, a JSON API, an old Drupal site, while tracking what it imported so you can re-run or roll it back. A migration is a YAML definition with three parts: a source, a process pipeline, and a destination.
Import external data into content entities repeatably. The worked example imports two articles from data embedded in the migration; the same shape scales to real sources by swapping the source plugin.
Migrate is not the only way to create entities in bulk, and for a one-time import of a manageable set it is overkill: a plain entity-creation loop (Create, load, and query entities) wrapped in a batch or queue for volume (Background processing) is far less machinery. Migrate earns its YAML when the import must be repeatable: it tracks every row it creates, so you can re-run against updated source data, resume after a failure, or roll the whole thing back.
Prerequisites
Section titled “Prerequisites”- A custom module, per Scaffold a module.
- The core Migrate module (
ddev drush pm:install migrate). Drush itself provides themigrate:status,migrate:import, andmigrate:rollbackcommands used below. - Entities, bundles, and fields for the destination side.
Write the migration
Section titled “Write the migration”Migrations live in your module’s migrations/ directory as ID.yml. The embedded_data source carries its rows inline, ideal for learning and for fixed lookup data; the process keys map source fields to destination fields; the entity:node destination saves each row as a node.
# A migration has three parts: a source that yields rows, a process# pipeline that maps source fields to destination fields, and a# destination that saves each row as an entity.id: howto_articleslabel: 'Import articles from embedded data'migration_tags: - howto
source: # embedded_data carries its rows inline; swap for csv, url, or a custom # source plugin against a real system. plugin: embedded_data data_rows: - id: 1 headline: 'Welcome to the site' summary: 'Our first imported article.' - id: 2 headline: 'Second post' summary: 'Another row from the source.' ids: id: type: integer
process: # destination_field: source_field, or a pipeline of process plugins. title: headline 'body/value': summary 'body/format': plugin: default_value default_value: basic_html
destination: plugin: 'entity:node' default_bundle: articleHow the three parts connect:
- Source yields rows and declares which fields form each row’s unique
ids. - Process maps each destination field.
title: headlineis a direct copy; a nested key likebody/valuetargets a field property; a process plugin (default_value, here) transforms or supplies a value. Many plugins can chain into a pipeline. - Destination names the entity type, with
default_bundlefor the bundle.
Rebuild caches so Drupal discovers the file: ddev drush cr.
Run it
Section titled “Run it”ddev drush migrate:status howto_articles # see counts and stateddev drush migrate:import howto_articles # run itddev drush migrate:rollback howto_articles # undo itThe Migrate API records a map from source IDs to created entities. That map prevents duplicates: re-running migrate:import skips rows already imported, and ddev drush migrate:import howto_articles --update re-applies the current source data to them. A rollback deletes exactly what the migration created.
Verify
Section titled “Verify”After importing, the nodes exist:
ddev drush php:eval "echo count(\Drupal::entityTypeManager()->getStorage('node')->loadByProperties(['type' => 'article']));"prints 2. The example module’s kernel test runs the migration and asserts both articles are created.
Variations
Section titled “Variations”Real sources
Section titled “Real sources”Swap embedded_data for csv (contrib Migrate Source CSV), url (for JSON and XML feeds, from contrib Migrate Plus), or a custom source plugin in src/Plugin/migrate/source/ when you query a system Migrate does not already speak.
Migrating from older Drupal
Section titled “Migrating from older Drupal”The Migrate Drupal module provides source plugins and upgrade paths for Drupal 6 and 7 sites, and the upgrade UI and the contrib Migrate Upgrade module’s drush migrate:upgrade build on the same API shown here. Migrate Drupal is deprecated in Drupal 11.4; its lifecycle entry on drupal.org explains where it goes next.
Related
Section titled “Related”- Plugins for writing custom source and process plugins.
- Create, load, and query entities for creating entities in code when a full migration is not warranted.
- Background processing for batching a hand-written import.
- Configuration management for moving configuration (structure) rather than content.
- The
migrate API topicon api.drupal.org.
Was this page helpful?
Drupal is a registered trademark of Dries Buytaert.