How to Migrate from Strapi 4 to Strapi 5: Complete Guide

Strapi 5 brings significant improvements over Strapi 4. Discover how to migrate your project safely with this step-by-step guide.

November 5, 2025

How to Migrate from Strapi 4 to Strapi 5: Complete Guide

Migrating to Strapi 5 is a full rework, not a version bump. This guide walks you through every essential step — including the new Document ID model, Document Service API, and plugin migration — with real examples and upgrade instructions.


1. Prepare your environment

Step 1 — Backup

Back up your Strapi 4 project:

# Save your DB pg_dump mydb > backup.sql # Backup source code cp -r my-strapi4-project my-strapi4-backup

Step 2 — Note custom code

List everything custom:

  • /src/plugins

  • /src/extensions

  • Modified controllers/services

Step 3 — Check versions

  • Strapi 5 requires Node.js ≥ 18.

  • Ensure your dependencies are compatible (npm outdated).


2. Create a clean Strapi 5 project

You cannot “upgrade in place”. Instead, create a new project:

npx create-strapi-app@latest my-strapi5-project

Then copy over:

  • /src/api (content types, controllers, services)

  • /src/plugins (custom ones only)

  • /config (migrated progressively)

You’ll refactor them step by step below.


3. Migrate Content Types

Step 1 — Adjust schemas

Open one of your Strapi 4 schema files, e.g.:

Strapi 4:

{ "kind": "collectionType", "info": { "displayName": "Article" }, "attributes": { "title": { "type": "string" }, "content": { "type": "text" }, "author": { "type": "relation", "relation": "manyToOne", "target": "api::user.user" } } }

Strapi 5:

{ "kind": "collectionType", "info": { "displayName": "Article" }, "options": { "draftAndPublish": true }, "attributes": { "title": { "type": "string" }, "content": { "type": "text" }, "author": { "type": "relation", "relation": "manyToOne", "target": "api::user.user" } } }

Changes:

  • Use documentId internally (Strapi manages this automatically — you no longer use numeric id).

  • Draft & Publish is now integrated into all content types.


4. Migrate from Entity Service → Document Service

Strapi 4 used:

await strapi.entityService.findMany('api::article.article', { filters: { title: 'Hello' } });

Strapi 5 now uses:

await strapi.documents('api::article.article').findMany({ filters: { title: 'Hello' }, status: 'published', // "draft" or "published" });

Step 1 — Search & Replace

Globally replace strapi.entityService with strapi.documents.

Step 2 — Update IDs

Replace references to .id with .documentId if you manually use identifiers in code.

Step 3 — Adjust queries

  • publishedAt is replaced by status: 'published'.

  • Pagination and filters are almost identical, but more consistent.


5. Update Plugins

Step 1 — Remove helper-plugin

@strapi/helper-plugin is deprecated. Replace all imports:

import { request } from '@strapi/helper-plugin';

import { request } from '@strapi/strapi/admin';

Step 2 — Adapt plugin API

Your custom plugin should now have:

src/plugins/my-plugin/ ├── strapi-admin.js ├── strapi-server.js └── package.json

In strapi-server.js:

export default () => ({ register() {}, bootstrap() {}, });

Step 3 — Use Document Service

Update all data logic in plugins to use strapi.documents() instead of entityService.


6. Update Config & Middlewares

Configuration files changed syntax slightly.

Strapi 4:

module.exports = ({ env }) => ({ host: env('HOST', '0.0.0.0'), port: env.int('PORT', 1337), });

Strapi 5:

import { defineConfig } from '@strapi/strapi'; export default defineConfig({ host: process.env.HOST || '0.0.0.0', port: parseInt(process.env.PORT) || 1337, });

Do the same for:

  • /config/database.js

  • /config/middlewares.js

  • /config/plugins.js


7. Migrate Data

If your content is simple (no relations, few entries), export & reimport:

# Export from Strapi 4 curl https://strapi4.example.com/api/articles?populate=* > articles.json

Then use a small script to push them to Strapi 5:

const axios = require('axios'); const data = require('./articles.json'); for (const item of data.data) { await axios.post('https://strapi5.example.com/api/articles', { data: { title: item.attributes.title, content: item.attributes.content, }, }); }

For larger databases, you’ll need to write SQL migration scripts adding documentId columns.


8. Reconfigure Roles & Permissions

In Strapi 5:

  • Roles and Permissions are tied to Document lifecycle actions.

  • Each content type now supports actions like read, create, publish, unpublish.

After migration:

  • Go to Settings → Roles & Permissions.

  • Re-enable access for each route.

  • Re-test your public/private API endpoints.


9. Test and Deploy

npm run develop

Check:

  • Admin panel loads.

  • All content types appear.

  • APIs respond with the new documentId.

  • Plugins work without helper-plugin errors.

Then deploy following Strapi 5’s official deployment guide.

Do you have a Web Development project? Let's discuss it 🚀

Set up a Web application