Strapi, the leading open-source headless CMS, has always been a favorite among developers. It is known for its great features, such as modern API creation, content management and collaboration, effortless coding, and intuitive publishing. With the release of Strapi 5, there are now a host of exciting new features and improvements.
Whether you're migrating from a previous version or starting fresh, as a developer, understanding these changes is important to leveraging Strapi 5's full potential. Here are the Top 10 Changes developers need to know about in Strapi 5.
We are glad to say that data.attributes
are no longer used. API responses are now simplified and flattened.
In Strapi v4, requested content data are wrapped inside an attributes
key. A typical API response of v4 would look like this:
1// Strapi v4 API Response:
2{
3 "data": {
4 // system fields
5 "id": 14,
6 "attributes": {
7 // user fields
8 "title": "Article A",
9 "relation": {
10 "data": {
11 "id": "clkgylw7d000108lc4rw1bb6s",
12 "attributes": {
13 "name": "Category A"
14 }
15 }
16 }
17 }
18 },
19 "meta": {
20 "pagination": {
21 "page": 1,
22 "pageSize": 10
23 }
24 }
25}
In Strapi 5, the attributes are no longer nested and are present directly in the data object. The Content API returns attributes of requested content without wrapping them in an attributes object:
1// Strapi v5 API Response
2{
3 "data": {
4 // system fields
5 "documentId": "clkgylmcc000008lcdd868feh",
6 "locale": "en",
7 // user fields
8 "title": "Article A",
9 "relation": {
10 // system fields
11 "documentId": "clkgylw7d000108lc4rw1bb6s",
12 // user fields
13 "name": "Category A"
14 }
15 },
16 "meta": {
17 "pagination": {
18 "page": 1,
19 "pageSize": 10
20 }
21 }
22}
Notice the introduction of the documentId
, which we will discuss shortly.
✏️ Note To use the old format of v4 in your Strapi 5 project, set the
Strapi-Response-Format: v4
in your project. We will see this in action below.
To use the previous Strapi v4 response, you will have to set the header of your response to Strapi-Response-Format: v4
.
When we fetch an entry in Strapi 5, this is the response we get:
As we can see in the image above, the response header doesn't contain the Strapi-Response-Format: v4
header. Also, notice the absence of the attributes
object in the response.
Let's add it to the Strapi-Response-Format: v4
header.
From the image above, we can see that when we add the Strapi-Response-Format: v4
to our API request headers, we will get the response in Strapi v4 format.
In Strapi 5, documents are introduced. Documents are all the entities that make up the different locales, drafts, historical versions, etc.
Using the Entity service, grouping and managing every bit of the same piece of content, such as localization, publication status, and content history, became very complicated and inefficient.
Some of Strapi 5's new features, including Draft and Publish with Content History, are made possible and efficient through the Document service. Otherwise, complex processes like an extra Join table would be created, making queries complex and slow.
Here is an example of how a query could be made using the Entity Service instead of the Document Service.
Using the Entity Service
1// Using the Entity Service
2strapi.entityService.findMany(uid, {
3 fields: ["id", "name", "description"],
4 populate: ["author", "comments"],
5 publicationState: "preview",
6});
Using the Document Service:
1// Using the Document service
2strapi.documents(uid).findMany({
3 fields: ["id", "name", "description"],
4 populate: ["author", "comments"],
5 status: "draft",
6});
In the first code, the strapi.entityService.findMany()
is used to find multiple entries that match the provided parameters. The publicationState
parameter is a string that specifies the publication state of the entries to return. In this case, it is set to "preview", which means both draft entries and published entries will be returned (more details here).
In the second code, the status
parameter is a string that specifies the publication status of the documents to return. In this case, it is set to "draft"
, which means only draft documents will be returned (source).
In terms of syntax, the Document Service and Entity Service are very similar. However, in the new Strapi 5, there are new functions such as count()
, which we will see soon.
→ You can check out the Entity Service API to Document Service API migration reference.
documentId
instead of Id
In v4, you would want to make use of the id
of an entry for every API call (REST API and GraphQL API. This was possible through the use of the Entity Service API.
For instance, you would want to get an entry by its id
and get the following response:
id
is used in Strapi v4 API calls
However, in Strapi 5, using the documentId
, the following response is returned:
Use documentId
for every API call in Strapi 5.
API calls to entries might still include an id
field in their response, especially with the Document Service API. But it's recommended that you start using documentId
instead of id
, as it will ease handling the transition to future Strapi versions.
In Strapi 4, applications could be created using TypeScript. An experimental typed system for APIs and automated typed generation for content types and components were also included.
With TypeScript support, Strapi 5 improved the type system with pure type names for APIs and better namespace organization. This will allow you to import the namespaces that you need using the correct types for your API. It also means that each type from the type system has a description and example of how to use it.
1import type {Data, Schema, UID, Utils} from "@strapi/strapi";
2
3// ...
4
5declare function fetch<T extends UID.Schema>(uid: T) : Data.Entity<T>;
6
7declare function getComponentSchemas(): Utils.String.Dict<Schema.Component>;
Strapi was initially written in plain JavaScript. In Strapi 5, the code base is almost entirely written in TypeScript. One benefit is TypeScript support for user-facing APIs, which will improve over time.
Strapi TypeScript Code Migration
With 100% TypeScript support in Strapi 5, you won't need to check which method, filter, or data type is expected.
Data manipulation with TypeScript
From the image above, we can see that the Document Service API provides us with access to many methods provided by TypeScript support.
In Strapi 5, creating an entry while uploading a file is no longer possible. The recommended steps are done in two steps.
The first step to take is to upload the file. Once uploaded, make sure to get its id
. This will be the id
of the file for the entry.
1// upload file
2const file_upload_response = await fetch(`${StrapiURL}/api/upload`, {
3 method: 'post',
4 body: formData
5});
6
7// create entry with file id
8const id = file_upload_response[0].id
id
When you get the id
of the file as shown above, the next step is to create the entry using the id
of the file. An example is shown below:
1// upload file
2const file_upload_response = await fetch(`${StrapiURL}/api/upload`, {
3 method: 'post',
4 body: formData
5});
6
7// Get the uploaded file id
8const id = file_upload_response[0].id
9
10// create new entry using the file id
11const newEntry = {
12 data: {
13 entryName: data.name,
14 entryFile: id // file id
15 }
16}
17
18// Invoke API Call to create new entry
19const response = await fetch(`${StrapiURL}/api/{collection}`, {
20 method: 'post',
21 headers: {
22 'Content-Type': 'application/json',
23 },
24 body: JSON.stringify(newEntry),
25});
The introduction of Draft and Publish brought in some new functions for Document Service API. Here are a few of them.
status
query parameter.count()
publish()
unpublish()
discardDraft()
When you make a GET
request to fetch an entry in Strapi 5 using the status
parameter. You can choose between a published entry or draft version by specifying the status
query parameter as published
or draft
.
Imagine we have a draft and published versions of an entry as shown in the two images below: Draft Version of an Entry
Published Version of an Entry
We will get this when we make a GET
request to get the draft version.
Fetch Draft version in Strapi
You get this when you set the status
query parameter to published
.
Fetch Published Version in Strapi
✏️ Note The published version gets returned when you do not specify the
status
query parameter.
count()
The count()
function fetches the number of documents that match the provided parameters.
1await strapi.documents('api::restaurant.restaurant').count()
publish()
With the publish()
function, you can publish one or multiple locales of a document. This method only works if Draft and Publish is enabled.
1await strapi.documents('api::restaurant.restaurant').publish({
2 documentId: 'a1b2c3d4e5f6g7h8i9j0klm',
3});
Let's say we want to find an article written in one language, English, and published in another, French.
This is what you can do:
1// find the English version of the article
2const article = await strapi.documents("api::article.article").findFirst({
3 locale: "en",
4 filters: {
5 title: { $contains: "health" },
6 },
7 status: "draft",
8});
9
10// if article exists, publish it in French
11if (article) {
12 await strapi.documents("api::article.article").publish({
13 documentId: article.documentId,
14 locale: "fr",
15 });
16}
→ Learn more about the Document Service API along with its functions
Strapi 5 introduces the new Plugin SDK, a CLI tool. Although there are many ways to create plugins in Strapi, it is recommended that you use the Plugin SDK.
With the Plugin SDK, you do not need to set up a Strapi project before creating a plugin. You can create Strapi plugins in 3 steps:
Start by creating a new plugin.
npx @strapi/sdk-plugin init my-strapi-plugin
In the command above, the my-strapi-plugin
represents whatever you want to call your plugin.
cd /path/to/strapi/project
npx yalc add --link my-strapi-plugin && npm install
Ensure you are inside your Strapi project before running the npx
command above. Also, have yalc installed globally.
Also, recall that my-strapi-plugin
is the name of your plugin or package and not a folder.
With the plugin ready to be published, the next step is to build the plugin.
npm run build && npm run verify
With the command above, you can safely build and verify that the plugin is ready to be published.
→ You can check out the Plugin SDK reference to learn more.
Previously, Strapi used Webpack, a free and open-source module bundler for JavaScript.
Because performance and stability are very crucial, we switched to Vite. Here are some benefits Vite provided:
Strapi 5 uses Vite
The documentation page now has an AI-based chatbot with which you can interact and ask questions. This chatbot, powered by Kapa.ai, is designed to assist users by answering their questions in real time.
👋 Attention Please ensure you double-check the information provided by a chatbot.
LaunchPad is now the official Strapi’s new demo app, designed to showcase Strapi 5’s capabilities with a modern frontend and advanced features.
LaunchPad, the official Strapi Demo App
Built to replace FoodAdvisor, which has been your go-to demo app to explore Strapi’s capabilities, reaching 1K+ stars on GitHub, it comes with a modern tech stack:
Strapi 5 introduced other new and breaking changes. Here are a few resources you can check out.
Visit the Strapi Docs for more information on breaking changes.
Join Marc Roig and Rémi de Juvigny from Strapi to learn how to streamline your content workflow with Strapi 5's new Draft & Publish and Content History features.
In this video, Alex and JS from Strapi talk about the improvements in performance with Vite and TypeScript support.
Learn how to streamline your content workflow with Strapi 5's new Draft & Publish and Content History features with Marc Roig and Rémi de Juvigny from Strapi.
So far, we have looked at the new Strapi 5 and its interesting new features, such as Draft and Publish, Vite bundler, TypeScript support, and more. We also discussed some changes, such as the Document Service API replacing the Entity Service, the documentId
for API calls, the new AI-based chatbot, the new way of creating Strapi plugins using the Plugin SDK, the official new Strapi 5 demo app called LanchPad, and so on.
To learn more about the new Strapi 5, visit the Strapi headless CMS documentation page.
Thank you for all the community support and contributions.
Theodore is a Technical Writer and a full-stack software developer. He loves writing technical articles, building solutions, and sharing his expertise.