Updating repeatable components with the Document Service API is not recommended
Page summary:In Strapi 5, draft and published versions of a document use different numeric component
ids. Reusing anidfrom a published response to update the draft usually fails. Prefer replacing the full component array (or editing against draft ids) until a durable component identity ships.
In Strapi 5, it's not recommended to update repeatable components by reusing component ids from published API responses, because of how Draft & Publish interacts with the Document Service API.
This page is part of the breaking changes database and provides information about the breaking change and additional instructions to migrate from Strapi v4 to Strapi 5.
Breaking change description
In Strapi v4
You could partially update a repeatable component by passing its numeric id.
In Strapi 5
Documents use a stable documentId, but nested components still use status-local numeric ids. Publishing creates new component rows, so the draft and published versions of the same block have different ids. You cannot treat a published component id like a document-level identifier.
Migration
This section regroups useful notes and procedures about the introduced breaking change.
Notes
With Draft & Publish enabled, a typical Content API flow looks like this:
// Request: Content API returns the published version by default
GET /api/articles
// Response (published)
{
data: {
documentId: '…',
components: [
{ id: 2, name: 'component-1' },
{ id: 4, name: 'component-2' },
],
},
}
Updating with those published ids writes the draft, which has different component row ids:
PUT /api/articles/{documentId}
{
data: {
components: [
{ id: 2, name: 'component-1-updated' }, // published id, usually wrong for draft
],
},
}
This often fails with an error such as Some of the provided components in components are not related to the entity, because id: 2 is not linked to the draft entry. This is related to the fact that components and dynamic zones do not return an id in REST API responses.
The Document Service defaults to the draft on read/update, so in-place updates by id can work there (and in the Content Manager) when you use draft component ids. The trap is mixing published ids with draft writes.
Recommended workarounds
Until a durable nested identity is available, use one of the following.
1. Replace the full component array (preferred for Content API)
Omit component ids and send the complete desired list. Strapi recreates the components on the draft. This is the supported, non-fragile approach for REST/GraphQL clients that only see published data:
// Document Service
await strapi.documents('api::article.article').update({
documentId,
data: {
components: [
{ name: 'component-1-updated' },
{ name: 'component-2' },
],
},
});
// REST Content API
PUT /api/articles/{documentId}
{
"data": {
"components": [
{ "name": "component-1-updated" },
{ "name": "component-2" }
]
}
}
Include every component you want to keep. Entries omitted from the array are removed from the draft.
2. Edit against draft component ids (Document Service / admin-style)
If you control the backend (custom routes, scripts, Document Service), load the draft, then update using those ids:
const draft = await strapi.documents('api::article.article').findOne({
documentId,
status: 'draft',
populate: ['components'],
});
await strapi.documents('api::article.article').update({
documentId,
data: {
components: draft.components.map((component) =>
component.id === targetDraftId
? { id: component.id, name: 'component-1-updated' }
: { id: component.id, name: component.name }
),
},
});
Do not reuse ids from a default Content API GET (published) for this pattern.
3. Draft & Publish disabled
If Draft & Publish is disabled on the content-type, there is only one component row set, so id-based updates are less problematic. Prefer the full-array replace pattern anyway for simpler client code.