Recently I took part in creating a pretty simple website based on Strapi CMS. It’s called Pathways. This is a presentation website where data and statistics on vulnerabilities of women and their children in India, Kenia and Nigeria are being shown. There are a lot of complicated data to show so data structures are intricate. It was my first time dealing with Strapi and I would like to share the biggest surprise for me while creating a frontend application. What will it be? Read on!
When I started working on the project, the first problem I encountered was the endpoints responses data structure. It was simply weird. Lots of unwanted properties, everything was nested multiple times. I didn’t like that at all. I had to work something out. I found a simple solution pretty fast. Deleting unwanted properties and pulling out data to the parent properties. Everything looked easy but when I started implementing some methods to remap data, there was a problem dealing with models. It was a mess. Take a look at how it presents now.
Here we got an example response from API:
It’s based on main API response model. Take a look at property ‘data’ and its children properties. This is a typical schema when the ‘data’ property shows up. The ‘Meta’ property occurs only in the top-level objects.
In such a shape, I had to read data in JS or HTML with multiple nested properties, e.g. data.attributes.members[0].logotype.data.attributes.url. Unacceptable. The plan was to prepare a mapper to get rid of unwanted props like ‘data’, ‘attributes’ and flatten objects a little bit.
Here, a mapped example:
Looks better, right? So now how to remap such objects and deal with interfaces to keep everything type safe?
The first thing that came into my mind was mapper class as a singleton (Singleton pattern). It had to implement the interface ‘MapperBase’ with methods to be able to deal with different models in the same way. Then I could create a mapper object, and use it in API service methods to map and return prepared data. Here is how ‘MapperBase’ interface looks like:
And here's an example of a mapper class:
You can notice two methods: ‘mapFromApi’ and ‘mapArrayFromApi’. It’s because responses which start from an array have a little bit different structure and mapping needs to be done in another way. Now such a mapper can be used in another one like this:
And finally an example of usage in services which fetch data from API endpoints:
I didn’t note that there was a second complication. Models. First, we need to define a model for response to be sure how it will be returned from the API endpoint. The second, final model which we will remap the response into and use in JS/HTML.
Now, what does ‘FileDataResponse’ looks like? It will be hard to read, I know, but that was the only way I found it possible to be done.
What do we get in here? First of all, ‘ApiResponse’. This is a general model for every API response which returns a single set of data (not an array). It looks like this.
Second of all, ‘OmitType’. This is the helper model which is defined to override properties in the final data model.
Above I highlighted in orange lines what I’m trying to override. Final model ‘FileData’ and property ‘folder’ in it. Why do I need to override it? Simply because in response property ‘folder’ got a nested data structure which we will need to remap.
Do you remember the screen with ‘FileDataMapper’? There was an example of a nested mapper based on the ‘folder’ property. It is a perfect sample of the nested Strapi structure which I had to deal with.
Finally, let’s take a quick look into mapping sum up. Starting from ‘TeamMemberResponse’ which is a typical response from Strapi I need to use ‘TeamMemberMapper’ (shown in one of the above screens) to prepare the final data. Here is the result:
The road is complicated but the results are definitely satisfying. Now instead of reading ‘TeamMemberResponse.data.attributes.logotype.data.attributes.folder.data.attributes.path’ I can simply write ‘TeamMember.logotype.folder.path’. 10 vs 4. Worth it!
Strapi responses data structure is definitely complicated which doesn’t mean it is bad. It, of course, got some schemas to keep everything reusable which let the backend iterate over database data and prepare responses with a single algorithm. Unfortunately, such solutions might be troublesome for frontend programmers but as you saw everything might be done in a simpler or more complicated way. I bet this isn’t a perfect solution but it provides a reusable mechanism which results in more structured and readable code.
Wojciech Jakubek