Strapi Middlewares
📦 TransformCollectionTypes
Section titled “📦 TransformCollectionTypes”🧠 What does it do?
Section titled “🧠 What does it do?”This middleware enriches collection-type API responses in Strapi 5 by automatically injecting two types of related content when applicable:
related
– Based on shared tags between the current document and others of the same content type.latest
– The most recently published entries from the same content type.
This is primarily useful for content blocks like Related Articles
,
Latest News
, or More Like This
sections.
The middleware kicks in when:
- The API response contains data (i.e., a collection).
- The query includes a
filters.slug
parameter.
It then:
- Resolves the UID of the collectionType by slug.
- Detects tag relations (if any) and fetches related entries.
- Fetches the latest entries as a fallback.
- Populates key attributes (e.g., hero, salon, function) to allow richer display on the frontend.
🛠️ How to customize for your project
Section titled “🛠️ How to customize for your project”1. Custom populate
per content type
Section titled “1. Custom populate per content type”In the section:
if (uid === "api::vacancy.vacancy") { populate["salon"] = { populate: "*" }; populate["function"] = { populate: "*" };}
You can add more UIDs and define which fields to populate depending on the structure of your content type.
Example:
if (uid === "api::event.event") { populate["location"] = { populate: "*" }; populate["speakers"] = { populate: "*" };}
2. Customize how related items are selected
Section titled “2. Customize how related items are selected”The logic currently checks if the current entry has tags:
if (data[0].tags) { populate['tags'] = { populate: '*' }; relatedFilter['tags'] = { slug: { $eq: data[0].tags.map((tag: any) => tag.slug) } }; ...}
You can extend this to include other logic, like shared categories or authors.
Example:
if (data[0].category) { relatedFilter["category"] = { id: data[0].category.id };}
3. Customize the returned shape of related/latest
Section titled “3. Customize the returned shape of related/latest”This determines how the frontend receives the related
and latest
objects:
return { image: (hero && hero.image) ?? record.image, title: record.title, text: hero?.text ?? record.text, uid: findRoute.uid ?? null, publishedAt: record.publishedAt, slug,};
You can change or add fields here based on your frontend display needs.
Example:
return { image: hero?.image ?? record.image, title: record.title, description: record.description ?? record.text, slug, category: record.category?.title,};
📌 Summary
Section titled “📌 Summary”Feature | Description |
---|---|
related | Adds similar entries based on shared tags or fallback content |
latest | Always includes the latest entries from the same collection |
UID detection | Uses the link plugin to resolve UID from slug |
Populates | Can be customized per UID to include rich fields (hero, relations, etc.) |
Route mapping | Matches content entries with frontend slugs from the link service |
Easy to extend | Fully open to customization depending on project needs |
🧩 TransformLatestTeasers
Section titled “🧩 TransformLatestTeasers”Path: apps/cms/src/middlewares/components/teaser.latest-teaser.ts
Middleware Type: transform
for components inside flexContent
🧠 What does it do?
Section titled “🧠 What does it do?”This middleware enriches the teaser.latest-teaser
component inside a dynamic
zone (flexContent
) with actual content fetched from the CMS.
It reads the component’s latestTeasers
property (formatted as a pipe-delimited
string like uid|limit|sortBy
) and:
- Dynamically resolves the UID, item limit, and sorting field.
- Fetches documents of the specified UID from the CMS, sorted accordingly.
- Auto-populates fields like
hero
,tags
, andflexContent
if they exist. - Resolves the frontend slug from the
link
plugin. - Replaces the
latestTeasers
string with an array of teaser objects, ready for rendering.
🛠️ How to customize for your project
Section titled “🛠️ How to customize for your project”1. Customize populate
behavior per UID
Section titled “1. Customize populate behavior per UID”The buildPopulate(uid)
function checks if certain attributes exist in the
UID’s schema and only includes those in the populate object:
return { flexContent: { populate: "*" }, ...(attributes.hero && { hero: { populate: "*" } }), ...(attributes.tags && { tags: { populate: "*" } }),};
You can extend this with more specific or custom logic for your use case.
Example:
...(attributes.category && { category: { populate: '*' } }),
2. Control how teasers are rendered
Section titled “2. Control how teasers are rendered”The structure of the returned teaser object is determined here:
return { ...record, image: record.image ?? hero?.image, title: record.title ?? hero?.title, text: record.text ?? hero?.text, uid, slug,};
You can modify this to include or override fields specific to your frontend needs.
Example:
return { title: record.title, image: hero?.image, slug, summary: record.summary,};
3. Component Format Required
Section titled “3. Component Format Required”To use this transform, your component must include a property like:
"latestTeasers": "api::post.post|3|publishedAt"
Where:
api::post.post
= UID of the collection type3
= number of items to showpublishedAt
= field to sort by (descending)
📌 Summary
Section titled “📌 Summary”Feature | Description |
---|---|
Dynamic UID | Determines which collection to query from a pipe-delimited string |
Auto Populate | Populates hero , tags , flexContent , etc. based on contentType schema |
Slug Resolution | Uses link plugin to resolve slugs for rendering |
Frontend Ready | Injects clean teaser objects into flexContent |
Extensible | Easily extend with more populate fields or teaser structure |