A Guide on Populating Dynamic Zones & Media in Strapi using Controllers
Diving into Strapi can be an exciting journey, but certain aspects, such as handling media and dynamic zones, might appear daunting at first. Strapi, prioritizing performance, defaults to not populating these elements. In this blog, we’ll explore strategies to effectively work with media and dynamic zones in Strapi, overcoming the initial challenges for a smoother development experience.
While starting out my internship at MyCaptain, I was tasked with exploring (googling) CMS options available in the market that would fit our use case well. I started my research and by sheer luck, I stumbled on Strapi, which is an open-source headless CMS based completely on Javascript.
The setup was tedious and had a learning curve, but after understanding the basics (going through the documentation and browsing YouTube), The issue of adaptation friction was minimal as the APIs were easy and simple to consume while working across multiple platforms. We use controllers to populate fields as it helps us with granular control over what data should be served with which API.
We control on population of various fields in Strapi which you can read about more here.
Using the wildcard populate=*
we can fetch all the fields but even this doesn't populate media & dynamic zones, so to populate them we need to modify our controllers to fetch the data.
Controllers are like the directors behind the scenes in your website’s performance. They’re actually JavaScript files full of different actions, or tasks. When that data is requested, these actions jump into action, compute the result, and then sends it back.
Lets Deep dive into some use cases to understand how we can actually modify controllers and populate fields in Strapi
"use strict";
/**
* testing-app controller
*/
const { createCoreController } = require("@strapi/strapi").factories;
module.exports = createCoreController(
"api::testing-app.testing-app",
({ strapi }) => ({
async find(ctx) {
const { query } = ctx;
const entity = await strapi.entityService.findMany(
"api::testing-app.testing-app",
{
...query,
populate: {
proCard: {
populate: {
footerImage: true,
backgroundImage: true,
ctaRedirection: true,
},
},
exploreCard: {
populate: {
pointers: {
populate: {
image: true,
},
},
courseCards: true,
curriculumSection: true,
},
},
},
}
);
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
return this.transformResponse(sanitizedEntity);
},
})
);
Breaking this down, we’ll get to understand on how our data is getting populated.
We initialize s the createCoreController
function from the @strapi/strapi
module. This function is used to create a controller in Strapi.
const { createCoreController } = require("@strapi/strapi").factories;
Defining the Controller:
module.exports = createCoreController(
"api::testing-app.testing-app",
({ strapi }) => ({
async find(ctx) {
// Controller logic
const { query } = ctx;
const entity = await strapi.entityService.findMany(
"api::testing-app.testing-app",
// JSON Object Structure of Response
{
...query,
populate: {
proCard: {
populate: {
footerImage: true,
backgroundImage: true,
ctaRedirection: true,
},
},
exploreCard: {
populate: {
pointers: {
populate: {
image: true,
},
},
courseCards: true,
curriculumSection: true,
},
},
},
}
);
const sanitizedEntity = await this.sanitizeOutput(entity, ctx);
return this.transformResponse(sanitizedEntity);
},
})
);
In Strapi, controllers serve as the entry points for handling HTTP requests and orchestrating the interaction between the client and the application’s data. Here’s how we set up a controller for our testing-app
model:
Breaking it down:
module.exports
: This line exports the controller module, making it accessible to other parts of our application.createCoreController
: Strapi provides this function to create custom controllers tailored to specific content types or models."api::testing-app.testing-app"
: This string identifies the content type or model associated with our example controller, indicating it's related to thetesting-app
.({ strapi }) => ({ ... })
: This function takes thestrapi
object as a parameter, granting access to Strapi's services and utilities.async find(ctx) { ... }
: This method defines a controller action responsible for handling HTTP GET requests. Thectx
parameter encapsulates the request context, allowing us to interact with the request data and respond accordingly.
Handling GET Requests
Within the find
method, we implement the logic to process GET requests and retrieve data from the specified content type or model. This involves querying the database and formatting the response data appropriately for the client.
Using Strapi Services
The strapi
object provides access to various services, such as entityService
, which facilitates interactions with the underlying database. These services offer methods for querying, creating, updating, and deleting data, enabling us to perform CRUD operations within our controller logic.
Customizing Query Options
By utilizing methods like findMany
from the entityService
, we can customize query options to refine the data retrieval process. This includes specifying filters, sorting criteria, and populating related data structures based on the client's request parameters.
Returning Response
Once we’ve obtained the requested data, we sanitize and format it as needed before sending it back to the client as an HTTP response. This step ensures that the response adheres to our application’s specifications and meets any security requirements.
In summary, our controller acts as the intermediary between incoming requests and the application’s data layer, handling GET requests efficiently and providing clients with the requested data in a structured format.
Let’s look at how we control the data within the API as well:
// JSON Object Structure of Response
{
...query,
populate: {
proCard: {
populate: {
footerImage: true,
backgroundImage: true,
ctaRedirection: true,
},
},
exploreCard: {
populate: {
pointers: {
populate: {
image: true,
},
},
courseCards: true,
curriculumSection: true,
},
},
},
}
using the populate keyword, we define the components, the nestings also require you to add the populate keyword, which is why the structure of your components should be well-defined.
Using these strategies, Strapi allows us to have microscopic control over the populated data within our API requests, helping optimise queries and removing unnecessary items from the response itself.