Migrating Drupal to Strapi

Migrating Drupal to Strapi
Photo by Jan-Niclas Aberle / Unsplash

After a few years of pain, I've finally resolved to move the website of a client from Drupal (one old instance merely survived to the migration from v7 to v8, but with a high cost in term of stability) to a Strapi + Astro setup. I'm still working on it, but I want to write down a few notes about the most difficult step: migrate contents.

Of course each migration is completely different to the other, as each website has his own Drupal's custom types to map and move to Strapi, but some suggestion is valid in every case:

  • to serialize and move contents, I've prepared in Drupal some Views REST exports to "simulate" an API. Easier and faster than directly access the raw database. The generated JSON can be easily fetched and parsed with a PHP script, or whatever
  • it is not possible to set the created_at nor published_at dates of contents through the Strapi API. As a result, all nodes have the current date as creation date. In my PHP migration script (using this package to integrate with Strapi), I've managed to manually fix the dates directly into the database once attributes of each node have been pushed, something as
use MBVienasBaitas\Strapi\Client\Client;
use MBVienasBaitas\Strapi\Client\Contracts\Requests\Collection\StoreRequest;
use MBVienasBaitas\Strapi\Client\Contracts\Requests\Options\OptionJsonBody;

$url = 'https://my.strapi.instance:4242';
$token = 'MYSTR4P1T0K3N';
$client = new Client($url, $token);
$endpoint = $client->collection("news");

$db_path = '/path/to/strapi/database.db';
$database = new \SQLite3($db_path);
$database->busyTimeout(1000);

foreach($contents_from_drupal as $content) {
    $data = [
        "Title" => $content->title,
        "Slug" => $content->view_node,
        "Body" => $content->body,
    ];

    $request = StoreRequest::make(
        new OptionJsonBody([
            "data" => $data,
        ]),
    );

    $response = $endpoint->store($request);
    $new_document_id = $response["data"]["documentId"];

    $query = sprintf("UPDATE news SET created_at = %s, updated_at = %s WHERE document_id = '%s'", $content->created * 1000, $content->created * 1000, $new_document_id);
    $database->exec($query);

    $query = sprintf("UPDATE news SET published_at = %s WHERE document_id = '%s' ORDER BY id DESC limit 1", $content->created * 1000, $new_document_id);
    $database->exec($query);
}
  • an interesting fact emerging from the previous snippet: dates in the Strapi database are expressed in milliseconds. Be aware of this while moving values from one side to the other, or you will end up with impossible values when querying Strapi's API (e.g. published_at set at date 1970-01-01)
  • another interesting detail to consider about dates: Strapi keeps the full history of each version of each content, and only the latest one version should have a published_at value set. If you intend to create the node, then - for example - attach a cover image to it or logically relate the new entity with something else (all actions triggering the creation of a new version into the DB), and finally adjust dates directly into the database, remember to alter the published_at value only in the database row having the highest id for your target document_id. If not, for some reason, you will not be able to retrieve your contents into the Strapi web interface: I've lost a whole day figuring out this behavior...
  • the hardest pill to shallow: raw HTML body contents stay raw HTML. It is too hard to convert decades of HTML copy&pasted from the wild web, or from Microsoft Word, or just manually formatted in a WYSIWYG editor, into the clean and elegant JSON blocks expected by Strapi's Rich Editor. After some try I gave up, installed a CKEditor plugin in Strapi (a "Community Edition" version) and transferred the original HTML as-is

Moving from Drupal to Strapi (and rewrite the whole HTML templates in Astro) is not a rapid task, but - at least, in my case - a few days of work will save me from the agony to keep an outdated, broken, instable and duct-taped instance of the old-style CMS.