Skip to main content

v4 plugin migration: Migrating the back end

This guide is part of the v4 plugin migration guide designed to help you migrate a plugin from Strapi v3.6.x to v4.0.x.

Migrating the back end of a plugin to Strapi v4 requires:

Depending on these steps, some actions can only be done manually while others can be performed automatically by scripts that modify the code, which are called codemods. The following table lists available options for each step of the migration:

ActionMigration type
Update Strapi packagesAutomatic or manual
Update content-types gettersAutomatic or manual
Update content-types relationsManual
Update configurationManual

Updating Strapi packages

🤓 v3/v4 comparison

Package names in Strapi v3 are prefixed by strapi-.

Strapi v4 uses scoped packages.

To migrate to Strapi v4, rename all Strapi packages from strapi-package-name to @strapi/package-name. This needs to be done in the package.json dependencies and anywhere the package is imported.

Strapi scoped packages can be updated automatically or manually.

Automatic Strapi packages update

caution

Codemods modify the plugin source code. Before running a command, make sure you have initialized a git repo, the working tree is clean, you've pushed your v3 plugin, and you are on a new branch.

To update Strapi scoped packages automatically:

  1. Use the update-package-dependencies codemod by running the following command:

    npx @strapi/codemods migrate:dependencies [path-to-strapi-plugin]
  2. Use the update-strapi-scoped-imports codemod by running the following command:

    npx @strapi/codemods transform update-strapi-scoped-imports [path-to-file | folder]

Manual Strapi packages update

To update Strapi scoped packages manually:

  1. Rename all Strapi packages (e.g. strapi-package-name) in package.json to @strapi/package-name
  2. Repeat for all instances where the package is imported.

Updating content-types getters

🤓 v3/v4 comparison

Strapi v3 models have been renamed to content-types in Strapi v4.

If the plugin declares models, update the syntax for all getters from strapi.models to strapi.contentTypes. The syntax can be updated automatically or manually.

Automatic content-types getters update

caution

Codemods modify the plugin source code. Before running a command, make sure you have initialized a git repo, the working tree is clean, you've pushed your v3 plugin, and you are on a new branch.

To update the syntax for content-types getters automatically, use the change-model-getters-to-content-types codemod. The codemod replaces all instances of strapi.models with strapi.contentTypes in the indicated file or folder.

To use the codemod, run the following command in a terminal:

npx @strapi/codemods transform change-model-getters-to-content-types [path-to-file | folder]

Manual content-types getters update

To update the syntax for content-types getters manually, replace any instance of strapi.models with strapi.contentTypes.

tip

Strapi v4 introduced new getters that can be used to refactor the plugin code further (see Server API usage documentation).

Updating content-types relations

✔︎ Prerequisites

Updating content-types relations to Strapi v4 requires that the v3 models have been converted to Strapi v4 content-types (see converting models to content-types documentation).

🤓 v3/v4 comparison

Strapi v3 defines relations between content-types with the via, model and collection properties in the model settings.

In Strapi v4, relations should be explicitly described in the schema.json file of the content-types (see relations documentation).

If the plugin declares content-types with relations between them, migrating relations to Strapi v4 should be done manually in the schema of the content-types.

To update content-type relations, update the server/content-types/<content-type-name>/schema.json file for each content-type with the following procedure:

  1. Declare the relation explicitly by setting the type attribute value to "relation".

  2. Define the type of relation with the relation property.
    The value should be a string among the following possible options: "oneToOne", "oneToMany", "manyToOne" or "manyToMany".

  3. Define the content-type target with the target property.
    The value should be a string following the api::api-name.content-type-name or plugin::plugin-name.content-type-name syntax convention.

  4. (optional) In bidirectional relations, define mappedBy and inversedBy properties on each content-type.

Example of all possible relations between an article and an author content-types
./src/plugins/my-plugin/server/content-types/article/schema.json

// Attributes for the Article content-type
"articleHasOneAuthor": {
"type": "relation",
"relation": "oneToOne",
"target": "api::author.author"
},
"articleHasAndBelongsToOneAuthor": {
"type": "relation",
"relation": "oneToOne",
"target": "api::author.author",
"inversedBy": "article"
},
"articleBelongsToManyAuthors": {
"type": "relation",
"relation": "oneToMany",
"target": "api::author.author",
"mappedBy": "article"
},
"authorHasManyArticles": {
"type": "relation",
"relation": "manyToOne",
"target": "api::author.author",
"inversedBy": "articles"
},
"articlesHasAndBelongsToManyAuthors": {
"type": "relation",
"relation": "manyToMany",
"target": "api::author.author",
"inversedBy": "articles"
},
"articleHasManyAuthors": {
"type": "relation",
"relation": "oneToMany",
"target": "api::author.author"
}
./src/plugins/my-plugin/server/content-types/author/schema.json

// Attributes for the Author content-type
"article": {
"type": "relation",
"relation": "manyToOne",
"target": "api::article.article",
"inversedBy": "articleBelongsToManyAuthors"
},
"articles": {
"type": "relation",
"relation": "manyToMany",
"target": "api::article.article",
"inversedBy": "articlesHasAndBelongsToManyAuthors"
}

Updating plugin configuration

🤓 v3/v4 comparison

Strapi v3 defines plugin configurations in a config folder.

In Strapi v4, the default configuration of a plugin is defined as an object found in the config.js file or in the config/index.js file. These are then called from the entry file (see default plugin configuration documentation).

To handle default plugin configurations in Strapi v4 the recommended way:

  1. Create the server/config/index.js file containing an exported object.

  2. Within the config object:

    • Define a default key that takes an object to store the default configuration.

    • (optional) Add a validator key, which is a function taking the config as an argument.

      Example of a default plugin configuration
      ./src/plugins/my-plugin/server/config/index.js

      module.exports = {
      default: { optionA: true },
      validator: (config) => {
      if (typeof config.optionA !== 'boolean') {
      throw new Error('optionA has to be a boolean');
      }
      },
      }
  3. In the server/index.js file, import the configuration and export it.

    Example of a default entry file
    ./src/plugins/my-plugin/server/index.js

    // ...
    const config = require('./config');
    // ...

    module.exports = {
    // ...
    config,
    // ...
    };
  4. Make sure that Strapi is aware of the plugin's back-end interface exported from server/index.js by adding the following line to the <plugin-name>/strapi-server.js entry file:

    ./src/plugins/my-plugin/strapi-server.js

    module.exports = require('./server');