Any elegant way to reduce the Unified Response Format to handle in frontend?

I’m using version 4.0.6 and have a user management system implemented.

Thing is, when have a deep relation, accesing the data from the frontend can be really verbose.
for example accesing the name of a neighborhood of a client in a city is

city.data.attributes.client.data.attributes.neighborhood.data.attributes.name

So i have to map through to simplify the data, like this

const ticketList = tickets.data.map((ticket) => {
            ticket.attributes.id = ticket.id
            ticket.attributes.client.data.attributes.id = ticket.attributes.client.data.id
            ticket.attributes.client = ticket.attributes.client.data.attributes
            ticket.attributes.city.data.attributes.id = ticket.attributes.city.data.id
            ticket.attributes.city = ticket.attributes.city.data.attributes
            ticket.attributes.client.neighborhood.data.attributes.id = ticket.attributes.client.neighborhood.data.id
            ticket.attributes.client.neighborhood = ticket.attributes.client.neighborhood.data.attributes
            ticket.attributes.client.technology.data.attributes.id = ticket.attributes.client.technology.data.id
            ticket.attributes.client.technology = ticket.attributes.client.technology.data.attributes
            ticket.attributes.tickettype.data.attributes.id = ticket.attributes.tickettype.data.id
            ticket.attributes.tickettype = ticket.attributes.tickettype.data.attributes
            ticket.attributes.assignated.data.attributes.id = ticket.attributes.assignated.data.id
            ticket.attributes.assignated = ticket.attributes.assignated.data.attributes
            ticket.attributes.ticketdetails.data.forEach((ticketdetail) => {
              ticketdetail.attributes.id = ticketdetail.id
              ticketdetail = ticketdetail.attributes
            })
            return ticket.attributes
          })

Is there an option or a method to deal with this long nested responses in an more elegant way?
i was triying to made a function to flat the object but i thinks maybe there is a better way to do this.

2 Likes

I came here to ask this same question.

I have created a sanitizeApiResponse function that looks like:

export const sanitizeApiResponse = (response) => {
    if (!response || !response.data) {
        return undefined
    }
    if (Array.isArray(response.data)) {
        const sanitized = response.data.reduce(
            (acc, curr) => {
                const item = {
                    id: curr.id,
                    ...curr.attributes,
                }
                acc.push(item)
                return acc
            },
            [],
        )
        return sanitized
    }
    const sanitized = {
        id: response.data.id,
        ...response.data.attributes,
    }
    return sanitized
}

This transforms:

{
    data: [
        {
            id: 1,
            attributes: {
                item1: 'foo',
                item2: 'bar',
            },
        },
    ],
}

Into:

{
    id: 1,
    item1: 'foo',
    item2: 'bar',
}

It also works when the response doesn’t have an array in data :grinning_face_with_smiling_eyes:

This works pretty well for basic responses but the issue comes in when we have relations or components as we have to run the same function over and over again on each relation.

It’s a pretty nasty way to have to deal with things. It feels wrong that so much sanitization needs to be performed client side.

If anyone knows of a more elegant way then let us know!

1 Like

For those searching for a solution I found someone made a plugin for this GitHub - ComfortablyCoding/strapi-plugin-transformer: A plugin for Strapi Headless CMS that provides the ability to transform the API request or response.

1 Like

I done this :

const sanitizeResults = (response) => {
    if (!response || !response.data || !Array.isArray(response.data)) {
        return []
    }

    return response.data.map(item => {
        let sanitized = sanitizeObject(item)
        return sanitized
    })
};

const sanitizeObject = (item) => {
    let sanitized = {}
    if (!item || typeof item !== 'object') {
        return sanitized
    }
    for (const [key, value] of Object.entries(item)) {
        if (key === 'id') {
            sanitized[key] = value
        }
        else if (key === 'attributes') {
            for (const [keyAttribute, valueAttribute] of Object.entries(item[key])) {
                if (typeof item[key][keyAttribute] === 'object' && item[key][keyAttribute]) {
                    sanitized[keyAttribute] = sanitizeObject(valueAttribute.data)
                } else {
                    sanitized[keyAttribute] = valueAttribute
                }
            }
        }
    }
    return sanitized
}

export { sanitizeResults, sanitizeObject };

I’ve made some improvement to it.

function sanitizeObject(item) {
    let sanitized = {};
    if (!item || typeof item !== "object") {
        return sanitized;
    }
    if (Array.isArray(item)) {
        return item.map(sanitizeObject);
    }
    for (const [key, value] of Object.entries(item)) {
        if (key === "id") {
            sanitized[key] = value;
        } else if (key === "attributes") {
            for (const [keyAttribute, valueAttribute] of Object.entries(
                item[key]
            )) {
                if (
                    typeof item[key][keyAttribute] === "object" &&
                    item[key][keyAttribute]
                ) {
                    if ("data" in valueAttribute) {
                        sanitized[keyAttribute] = sanitizeObject(
                            valueAttribute.data
                        );
                    } else {
                        sanitized[keyAttribute] =
                            sanitizeObject(valueAttribute);
                    }
                } else {
                    sanitized[keyAttribute] = valueAttribute;
                }
            }
        } else {
            sanitized[key] = value;
        }
    }
    return sanitized;
}