Skip to main content

Entity Service API to Document Service API migration reference

In Strapi 5, the Document Service API replaces the Entity Service API from Strapi v4 (see breaking change description).

The present page is intended to give developers an idea of how to migrate away from the Entity Service API, by describing which changes in custom code will be handled by codemods from the upgrade tool and which will have to be handled manually.

❗️ Warning: Strapi 5 is not stable yet

It is strongly advised not to upgrade a critical or production-ready project from Strapi v4 to Strapi 5.

The content of migration resources might not be final yet. Migration resources are currently only provided to prepare you for the upgrade to Strapi 5 when it is released as a stable version.

Strapi 5 is currently only provided as a Release Candidate (RC) version and is not meant to be used in production yet.

Migration using the upgrade tool

When using the upgrade tool, a codemod is run and handles some parts of the entityService migration.

Caution

The codemod is only changing the function calls and some parameters. This can not be considered as a complete migration as the codemod will never be able to convert an entityId into a documentId.

Codemod scope

The following list explains what is automatically handled by the codemod (✅), what is not handled by the codemod and must be handled 100% manually (❌) and what will still require manual intervention after the codemod has run (🚧):

TopicHandled by the codemod?Manual steps to perform
Code structure✅ YesNothing.
The code structure is automatically migrated.
publicationState removed in favor of status✅ YesNothing.
The codemod automatically transforms it.
Usage of documentId instead of the Strapi v4 unique identifiers🚧 Partially:
  • The codemod adds the new documentId property to your code, since documentId is the new unique identifier to use in Strapi 5.
  • But the actual documentId value can not be guessed, so after the codemod has run, you will find __TODO__ placeholder values in your code.
👉 __TODO__ placeholder values need to be manually updated.

For instance, you might change
documentId: "__TODO__"
to something like
documentId: "ln1gkzs6ojl9d707xn6v86mw".
Update of published_at to trigger publication❌ Not handled.
👉 Update your code to use the new publish(), unpublish(), and discardDraft() methods of the Document Service API instead.

Examples of function calls migration

The following examples show how the codemod from the upgrade tool updates the code for various function calls.

findOne

Before:

strapi.entityService.findOne(uid, entityId);


After:

strapi.documents(uid).findOne({
documentId: "__TODO__"
});

findMany

Before:

strapi.entityService.findMany(uid, {
fields: ["id", "name", "description"],
populate: ["author", "comments"],
publicationState: "preview",
});

After:

strapi.documents(uid).findMany({
fields: ["id", "name", "description"],
populate: ["author", "comments"],
status: "draft",
});

create

Before:

strapi.entityService.create(uid, {
data: {
name: "John Doe",
age: 30,
},
});

After:

strapi.documents(uid).create({
data: {
name: "John Doe",
age: 30,
},
});

update

Before:

strapi.entityService.update(uid, entityId, {
data: {
name: "John Doe",
age: 30,
}
});

After:

strapi.documents(uid).update({
documentId: "__TODO__",
data: {
name: "John Doe",
age: 30,
}
});

delete

Before:

strapi.entityService.delete(uid, entityId);


After:

strapi.documents(uid).delete({
documentId: "__TODO__"
});

count

Before:

strapi.entityService.count(uid);

After:

strapi.documents(uid).count();

Manual migration

  • Users who prefer to manually migrate can do so by replicating what the codemod does (see codemod scope and function calls examples for reference).

  • Plugin developers who use Entity Service decorators in their code must replace them by Document Service middlewares. The following example gives you an idea of how they work, and additional information can be found in the dedicated Document Service middlewares documentation:

    In Strapi v4:

    strapi.entityService.decorate((service) => {

    return Object.assign(service, {

    findOne(entityId, params = {}) {
    // e.g., exclude soft deleted content
    params.filters = { ...params.filters, deletedAt: { $notNull: true } }
    return service.findOne(entityId, params)
    }
    });
    })

    In Strapi 5

    strapi.documents.use((ctx, next) => {
    if (ctx.uid !== "api::my-content-type.my-content-type") {
    return next();
    }

    if (ctx.action === 'findOne') {
    // customization
    ctx.params.filters = { ...params.filters, deletedAt: { $notNull: true } }
    const res = await next();
    // do something with the response if you want
    return res;
    }

    return next();
    });
  • Update your custom code for findMany() on single types, taking into account that:

    • In Strapi v4, the findMany() function returns a single item when called on a single type.
    • In Strapi 5, the findMany() function is generic and always returns arrays, whether called on a single type or on a collection type. To get data for a single type with a findMany() call, extract the first item from the returned array.