Why am I getting data in attributes object from API response when all examples I see do not have this?

This is what I’m getting. Only the id is not in a the attributes object. None of the examples I have seen do this.

"data": [
"id": 2,
"attributes": {
"total_votes": 0,
"createdAt": "2021-12-31T19:32:30.231Z",
"updatedAt": "2021-12-31T19:32:30.231Z",
"content": "This is a prompt"
"id": 3,
"attributes": {
"total_votes": 0,
"createdAt": "2021-12-31T23:58:15.524Z",
"updatedAt": "2021-12-31T23:58:15.524Z",
"content": "This is a prompt 2"
"id": 4,
"attributes": {
"total_votes": 0,
"createdAt": "2021-12-31T23:58:29.970Z",
"updatedAt": "2021-12-31T23:58:29.970Z",
"content": "This is a prompt 3"
"meta": {
"pagination": {
"page": 1,
"pageSize": 25,
"pageCount": 1,
"total": 3

Since Strapi v4 responses are different. Now all attributes are grouped inside ‘attributes’.

Except ID but yes, it’s the new v4 structure

Is there a way to revert to how Strapi 3 returned data? Is there a reason it changed?

And yes - would be nice if the examples here showed this change.


Feel free to tell me to RTFM if this was written about somewhere - but post the link :wink:


1 Like

I also don’t understand. @strapi Team please explain why this decision was made.
It makes development way more complicated.


I agree with you. When we do abstraction on class models, it forcing us about some static fields. it is bad idea, please tell us why did you that?
-Notice: there is a plugin that name is transformer but it shouldnt be required.


It’s become quite difficult because the entire set of responses has changed. Not just that even the relations have data and attributes fields which make it quite complicated to deserialise the responses.

I thought v4 would be a change in the structure/working of Strapi but the change is huge in terms of outputs. It’s breaking lots of stuff for me.

It’s good that I still have the Strapi v3 instance installed on a separate server, else it’s pretty hard to update everything.

1 Like

I didn’t see anything regarding why the team made that decision. Is it explain somewhere else ? It’s breaking a lot of stuff here too I registered precisely for that.


Oh well, found it here : Discussion regarding the complex response structure for REST & GraphQL (Developer Experience)

1 Like

@BLu On Strapi V4 the get response do not return all the data by default, but you can populate all the data you want in your response, you can find it here:

You can use this plugin to get the API response you need :wink:

I had the same problem, and I ended up making a script that parses the GraphQL complex response into a simple one while keeping the right typescript types. The idea is to use it in the frontend.

Find it here: Flatten Strapi 4's response JSON · GitHub

Can we maybe have an answer from the staff ?
Because I really don’t understand why adding these complexe structure with attributes and data…
And it’s on purpose, because if we didn’t use the transformResponse function, there is not data…

//Override find examples :
async find(ctx) {
    await this.validateQuery(ctx);
    const sanitizedQueryParams = await this.sanitizeQuery(ctx);
    const { results, pagination } = await strapi
    const sanitizedResults = await this.sanitizeOutput(results, ctx);

// transformResponse add attributes....
    return this.transformResponse(sanitizedResults, { pagination });
//--> Output with attributes

//Custom return without any attributes
    const activitiesMeta = {
      data: sanitizedResults,
      meta: pagination,

    return activitiesMeta;