Migrating an entry

Posted in October 14, 2021

In this tutorial, we are going to cover how to migrate the articles within our WordPress site to the Headless CMS Storyblok.

1st step. Create Content Type: post

The first step of the migration will be to create the Content Types in Storyblok, equivalent to the ones we have in our WordPress admin panel.

Basically, we need to replicate the schema, that WordPress has already defined, in our Storyblok space.

Go to the REST API documentation to check the reference posts schema.

Looking at the schema reference, you will need to check the fields you want to migrate to Storyblok. Not all fields will be needed, only the ones that have useful content for your site, like:

  • date: The date the object was published, in the site’s timezone (remember to set the timezone to the same one in your Storyblok space).

  • title: The title for the object.

  • slug: An alphanumeric identifier for the object unique to its type.

  • featured_image: The ID of the featured media for the object (the wordpress-importer to Storyblok plugin will import the image for you).

  • excerpt: The excerpt for the object.

  • content: The content of the object.

  • categories: The terms assigned to the object in the category taxonomy.

For this example, it is important to note that when we are creating a Content Type in Storyblok, by default it comes with automatic fields such as first_published_at, name, slug, etc.

To facilitate the migration and not duplicate fields, we will reuse them in the mapping step.

Now that we are clear on the WP posts schema, we need to define the schema for the new post Content Type in Storyblok:

  • date will be the default field: first_published_at.

  • title will be the default field: name.

  • slug will be the default field: slug.

  • featured_image will be a new field of type: Assets > Images.

  • excerpt will be a new field of type: Rich-text.

  • content will be a new field of type: Rich-text.

  • categories will be a new field of type: Multi-Options > source: stories > folder: categories/.

2nd step. Create the migration script

Prerequisites

WP version > 4.7.0 (public release of REST API).

REST JSON API publicly available. Have access to /wp-json.

WP Permalinks selected by the name of the entry and not as plain (to maintain parent-child pages relationship).

Node ≥ 14.

Once we have all the prerequisites ready to start working on the migration, it is time to clone the plugin repository that will be in charge of the migration, and start defining the script that we will run for the migration.

The steps to follow to create the script are:

  • Clone the wordpress-importer plugin: git clone <https://github.com/storyblok/wordpress-importer.git>.

  • Install the NPM dependencies: npm install

  • Create a migrate-wp-to-storyblok.js file in the root of the project.

  • Add a command in the package.json to run the script when finished: "migrate": "node ./migrate-wp-to-storyblok.js"

Now that we have it on our computer, the next thing to do is to connect it to our Storyblok space and read the WP content from its API. To do this:

  • Copy/Paste the initial code in the .js file:

    import { Wp2Storyblok } from './index.js'

    const wp2storyblok = new Wp2Storyblok('[YOUR-REST-API-URL]', { token: [YOUR-STORYBLOK-TOKEN], // My Account > Personal access tokens space_id: [YOUR-STORYBLOK-SPACE-ID], // Settings blocks_mapping: [], content_types: [], })

    wp2storyblok.migrate()

  • Replace the [YOUR-*] by:

    • [YOUR-REST-API-URL]: Your REST API publicly available (Ex: https://wp2.storyblok.com/wp-json)

    • [YOUR-STORYBLOK-TOKEN]: OAuth token in the Storyblok space (My Account > Personal access tokens)

    • [YOUR-STORYBLOK-SPACE-ID]: Space_id in the Storyblok space (Settings > General)

You already have what you need to connect to your Storyblok space, so we can start defining the relationship between the WP schema and the Storyblok schema in the content_types array.

3rd step. Map the defined schemas

To do the mapping you first need to know how a content_type object is formed in the script and the field types it allows defining:

  • The basic object looks like:

    { name: 'posts', // Name of the post type in WP new_content_type: 'post', // Name of the Content Type in Storyblok folder: 'articles', // Optional - Inside which folder you want to migrate the content taxonomies: [ { name: 'categories', field: 'categories', type: '' } ], schema_mapping: {} // Relation between WP field and Storyblok field },

  • The type of fields you can add are:

    • simple field, such as title.

    • sub-property of a field, like in this case the URL of the feature image.

    • A field migrated to a nested block in Storyblok (object format).

    // SIMPLE field "title": "name", // "Field in WP": "Field in Storyblok"

    // SUB-PROPERTY field "_links.wp:featuredmedia.0": "content.preview_image",

    // NESTED BLOCK field "content": { field: 'content.content', // Field name in Storyblok component: "rich-text", // Component name inside the above field component_field: "content" // Field name inside the component you are migrating to }

Now we have to identify what type each field is and form our object. This would be the result:

{
      name: 'posts',
      new_content_type: 'post',
      folder: 'articles',
      taxonomies: [
        {
          name: 'categories',
          field: 'categories',
          type: 'relationship'
        }
      ],
      schema_mapping: {
        date: 'first_published_at', // SIMPLE
        title: 'name', // SIMPLE
        slug: 'slug', // SIMPLE
        '_links.wp:featuredmedia.0': 'content.featured_image', // SUB-PROPERTY
        excerpt: 'content.excerpt', // SIMPLE WP : SUB-PROPERTY Storyblok
        categories: 'content.categories', // SIMPLE WP : SUB-PROPERTY Storyblok
        content: 'content.content', // SIMPLE WP : SUB-PROPERTY Storyblok
      }
},

For any field that we have created ourselves, and is not a default field, in the Content Type in Storyblok, we have to add ‘content.’ before the field while mapping.

And finally, we should specify the taxonomy relationship that we usually find in the type: posts.

For example, imagine that you have created an article with one or more categories, then you want to have the relation with the category Content Type. The desired mapping would look like this:

content_types: [
   {
      // name: 'posts',
      // new_content_type: 'post',
      // folder: 'articles',
      taxonomies: [
        {
          name: 'categories', // Taxonomy's name in WordPress
          field: 'categories', // Source field's name in WordPress
          type: 'relationship' // The taxonomy's type (Options explained below)
        }
      ],
      schema_mapping: {
        // Other fields
        categories: 'content.categories', // Field in WP : Field in Storyblok getting the content 
      }
   },
]

The taxonomy’s type could be set to:

  • value: to replace the taxonomy ID with the slug.

  • relationship (or leave empty): to import also the taxonomy entries as stories, and you want to link the taxonomy entry with an option or multi-option field by UUID.

One thing that will surprise you is that, even if you don’t create the Content Type for category in Storyblok, the migration plugin will still migrate the content.

And from the panel, you can choose which properties you want and which you don’t, and it will automatically create the Content Type for you.

In summary, after all these steps, the migration script should look like this:

import { Wp2Storyblok } from './index.js'

const wp2storyblok = new Wp2Storyblok('[YOUR-REST-API-URL]', {
  token: [YOUR-STORYBLOK-TOKEN],
  space_id: [YOUR-STORYBLOK-SPACE-ID],
  content_types: [
    {
      name: 'posts',
      new_content_type: 'post',
      folder: 'articles',
      taxonomies: [
        {
          name: 'categories',
          field: 'categories',
          type: 'relationship'
        }
      ],
      schema_mapping: {
        date: 'first_published_at', 
        title: 'name',
        slug: 'slug',
        '_links.wp:featuredmedia.0': 'content.featured_image',
        excerpt: 'content.excerpt',
        categories: 'content.categories',
        content: 'content.content',
      }
    },
  ],
})

wp2storyblok.migrate()

Posted in Entry Migration