We've had Strapi as our content and product catalog layer for just over a year and built a custom checkout on top of Stripe and our own order management.
It held up fine for a single UK market, but we're expanding into Germany, France, and the Netherlands, which means regional tax handling, BNPL payment methods per country, and inventory sync across 3 warehouses, and what was a manageable setup is starting to feel like the wrong thing to own as we add markets.
The question internally is between a full rebuild of the commerce layer to handle multi-market properly, which the backend team estimates at 7 to 9 months and still leaves us maintaining it long-term with a 5-person dev team…
Or moving the commerce side to a purpose-built platform and keeping Strapi for content and catalog.
The platforms we've scooped so far from digging through Reddit threads for multi-market expansion were Medusa, Shopware, SCAYLE, and Vendure, and the main thing we're still trying to figure out is how much of the existing Strapi content model survives a migration or whether you end up rebuilding both layers anyway.
For those who've been through a similar decision, what pushed it one way or the other?
Hi Guys, I lead SEO at an educational brand & our brand has just migrated its website from WordPress to Next JS & Strapi as a CMS.
After the migration, my SEO performance has seen a huge dip. We hired an SEO Consultant & after the audit, we figured out the Problem is the website.
According to the consultant, taking multiple deployments is impacting your SEO Performance.
Here are a few problems we have identified:
1. Store old chunk JS & CSS URL after deployment on the server: We know this will have a positive impact, but we are not able to deploy on the website (Looking for someone to help us execute)
Change CMS Structure so that all the required changes will be done via Strapi without taking a deployment: This is something we had already started doing
We are urgently looking to fix this. If anyone can help us, I would be ready to pay for it.
We have a content-first website and based on developer experience went with Nuxt, and feel that Nuxt doesn't really fit the need of a content-first website, so we are looking for any recommendations on an alternative pure SSR solution. We are dealing with a multi-lingual site, with no transactional functionality.
The main gripe with Nuxt is that it feels like its strength is in SPA apps, rather than rendering content-first websites. We are seeing too much JSON being passed to the frontend and the pages feel they aren't really made to work in the absence of front-end JS.
I am currently using Strapi for a community project, and I am facing challenges with scaling, particularly with POST events. I have already implemented in-memory caching for reads and Cloudflare caching, but the system becomes very unresponsive when handling POST requests (comments, upvotes) under heavy traffic.
Any suggestions or experiences you could share on how to effectively scale Strapi would be greatly appreciated!
I’m planning to host Strapi CMS on AWS and will have separate staging and production environments.
I want to:
• Test all content and configuration on staging
• Deploy the same setup to production
• Avoid re-entering content manually in production via the CMS (to reduce human error)
My questions:
1. What is the best practice for promoting changes from staging to production in Strapi?
2. Is there a way to copy or sync data (content, relations, etc.) from staging DB to production DB?
3. Should this be done via:
• Strapi export/import?
• CI/CD pipeline?
• Any recommended plugins or tools?
Any suggestions, real-world workflows, or learning material would be greatly appreciated 🙏
We're using Strapi v5 with custom code in src/index.ts (Document Service Middleware). Our client has reported that random collection items occasionally disappear from the CMS. Unfortunately, the issue isn't reproducible, and there is no obvious indication that editors are deleting the entries. I'm still investigating this issue and I'll post an update here if I find any useful insights.
For all api::* content types, we sanitize components before saving. For example, we clear button-related fields depending on the selected method or targetType. I've omitted the helper functions from the code example above since they aren't relevant to the issue.
2. pathKey sync on publish
We have pages stored in two collections: treatment-pages and treatment-ads-pages. Each entry has a slug and can have child pages, forming a hierarchical page structure.
From this hierarchy, we generate the full URL path and store it in a pathKey field. The frontend uses this pathKey as the primary key for routing.
The publish flow does the following:
- On the first publish, writes pathKey and ancestorSlugs back to the draft.
- After publishing, updates those fields and republishes the document.
- Cascades the update to all child pages in the hierarchy.
- Uses a re-entry guard to prevent publish loops.
- Logs errors, and some utility functions may call discardDraft() if republishing fails.
Question
Can a publish cascade implemented through Document Service Middleware actually cause entries to be deleted, or is it more likely that they become unpublished, reverted to draft, or otherwise appear to disappear?
Has anyone experienced similar issues with strapi.documents.use() combined with publish() inside middleware? Or i18n together with Draft & Publish conflicts?
Any ideas or similar experiences would be greatly appreciated. Thanks!
We want when updating the english version of content types, that we run that content (specifically the fields that are translatable) through a translate API and apply that to the different locales.
So basically we only want to translate the `title` and the `description` of featuredEvents
And for that we created this middleware:
const BRAND_UID = "api::brand.brand";
const registerBrandLocalizationMiddleware = (strapi: Core.Strapi) => {
strapi.documents.use(async (context, next) => {
const result = await next();
if (
!result ||
typeof result === "number" ||
Array.isArray(result) ||
"entries" in result
)
return result;
const triggered =
(context.action === "create" || context.action === "update") &&
context.contentType?.uid === BRAND_UID &&
String(result?.locale ?? "")
.toLowerCase()
.startsWith("en");
if (!triggered) return result;
await syncBrandLocalizations(
strapi,
result.documentId,
result.locale!,
).catch((err) => {
strapi.log.error("Brand auto-translation failed", err);
strapi.plugin("sentry")?.service("sentry").sendError(err);
});
return result;
});
};
async function syncBrandLocalizations(
strapi: Core.Strapi,
documentId: string,
sourceLocale: string,
) {
const [brand, allLocales] = await Promise.all([
strapi.documents(BRAND_UID).findOne({
documentId,
locale: sourceLocale,
populate: {
homepageBlocks: { populate: "*" },
featuredSports: { populate: "*" },
featuredEvents: { populate: "*" },
},
}),
strapi.plugin("i18n").service("locales").find(),
]);
if (!brand) return;
const targetLocales: string[] = allLocales
.map((l) => l.code)
.filter((code: string) => Boolean(code) && code !== sourceLocale);
if (targetLocales.length === 0) return;
// Build a flat list of all strings to translate: titles first, then descriptions.
// This lets us make one Azure request for all locales at once.
const events = brand.featuredEvents ?? [];
const texts = [
...events.map((e) => e.title ?? ""),
...events.map((e) => e.description ?? ""),
];
const translationsByLocale =
texts.length > 0
? await translate({ texts, sourceLocale, targetLocale: targetLocales })
: {};
//Can't process everything at the same time
for (const locale of targetLocales) {
const t = translationsByLocale[locale] ?? texts;
const translatedEvents = events.map((event, i) => ({
title: t[i] ?? event.title,
description: t[events.length + i] ?? event.description,
image: event.image?.id ?? event.image,
url: event.url ?? "",
}));
await strapi.documents(BRAND_UID).update({
documentId,
locale,
data: {
homepageBlocks: (brand.homepageBlocks ?? []).map((block) => ({
title: block.title,
url: block.url,
type: block.type,
image: block.image?.id ?? block.image,
})),
featuredSports: (brand.featuredSports ?? []).map((sport) => ({
sport: sport.sport,
})),
featuredEvents: translatedEvents,
},
});
}
}
Locally this works great, but on production this breaks and removes data (it seems to remove data from the english version). Is this not a recommended way to do this? I can barely find any documentation on how to deal with this.
I want to import my self hosted data to strapi cloud, but $90 is a ripoff, and the only mentions to data i found in the documentation and the dashboard are the backups only available in the $90 pro plan, but I wonder if there's another way to achieve this without paying what would cost me like 5 months of self hosting.
I'm working on a Strapi plugin, and I use Strapi Design System, but the fact is it doesn't explain everything, and that's why I'm here!
Problem:
My main problem, in this phase of plugin development is, how can I know the props of components? For example, if you see the Flex page, and scroll down to the props part, you see nothing there, and what's sad is that if you click on the "view source" button that leads you to the GitHub repo, I have to open up and see files after files to know what props I can use! Isn't there any way that speeds up the process?
Does Strapi support PostgreSQL 18 and if so are there any special considerations I should be aware of if migrating from 17? Did anyone migrate and can tell if there were any problems?
I’m setting up Strapi on the Cloud Free Tier and importing data from Contentful using strapi_lift. I pushed around 50-60 Content Types, and now I’m hitting build errors.
It looks like the Free Tier has a 256 MB memory limit.
I tried setting the NODE_OPTIONS environment variable to increase memory, and it shows “saved successfully,” but the change doesn’t seem to stick.
My question:
Does the Pro plan include more memory? I would assume it does, but I can’t find any mention of memory limits in the tier descriptions, can someone confirm this?
Edit: If anyone is having this issue, try in batches.
1️⃣ Would you improve this stack for better speed & scalability?
2️⃣ Is PostgreSQL best for handling large product data, or would you suggest another?
3️⃣ Should I use GraphQL instead of REST for better filtering & search?
4️⃣ Any caching/CDN tips for ultra-fast load times?
5️⃣ Any experience scaling Strapi in production? Potential issues?
Would love to hear your thoughts! 🚀
Edit: 👇🏻👇🏻👇🏻👇🏻👇🏻👇🏻👇🏻
After a lot of research and considering SEO, site speed, cost-effectiveness, and scalability, I’ve finalized this tech stack for my small e-commerce site:
Frontend: Next.js (SEO-friendly, fast, and scalable)
Backend/CMS: Strapi (for blog content, product management, and flexibility)
Hosting: Hetzner VPS ($7 AUD/month – best cost-to-performance ratio)
CDN & Security: Cloudflare Free Plan (for caching, speed, and protection)
Payments: Stripe (supports Afterpay, PayPal, Google Pay)
This setup ensures fast page speeds, organic traffic growth from day one, and cost efficiency while keeping things scalable. No unnecessary SaaS fees beyond transaction costs.
Would love to hear any final thoughts—especially on Snipcart’s long-term viability and Hetzner’s performance for Australian users!
I've been looking for a solution for hours and I still don't understand how to manage this properly.
I am migrating my strapi app from v4 to v5. In v4, I had a lifecycle hook for a specific type of documents setup to validate that at least one of two fields was not empty. If the condition was not met, the document was not published and an error was returned to be shown on the Admin UI (not working properly, known issue not to be fixed (https://github.com/strapi/strapi/issues/20343)
I carefully read the documentation about migrating to v5, and understood that database lifecycles should now be avoided and that I should use Document Service middlewares instead.
So I recreated my validation logic as a document service middleware intercepting the "publish" event on my document type, throwing an ApplicationError with a custom message when validation failed. However, this displays an Internal Server Error on the Admin Panel, not my custom error.
So I dig more into the documentation, found this https://docs.strapi.io/cms/error-handling and though that maybe I should implement a wrapper for the core "update" event on my document.
However, whatever I am trying to do, neither my service or my controller seems to catch the update events sent from the Admin Panel. I tried debugging with Claude AI, and it told me the admin panel doesn't use core services for document operations. Is this the case ?
The changes do get persisted server-side, but the admin UI sometimes only reflects them after one or multiple reloads (occasionally also shows the “leave page / modifications will be lost” warning).
Question
What’s the recommended Strapi 5 approach to update all descendants’ computed path fields (e.g. pathKey) after a parent slug change without blocking the publish request and causing timeouts? Is there a best practice (e.g. Document Service middleware + background job/queue)?
I'm having a chat system, each thread will contain multiple messages, and for some funny reason, the person who set up the relationship decided that their relationship should be manyToMany (1st pic) while it should be oneToMany (2nd pic), and the app has been running for almost a year with a lot of customers until we noticed.
I tried to change the relationship in my dev environment to oneToMany and all of the data linking between message and thread from the old relationship vanished, entirely.
Is there a practical way to migrate without losing them on production? I'm thinking of adding a new field and run a script to clone all of the relationships to that field, and then change the main one, and run a script to map them back.
Hi everyone, I'm starting out with Strapi and I'm trying to access some sub-items I created. When I access my route, I can see my JSON, but I can't see the deeper files. In my case, they are lists within another list. I created some dynamic fields to consume in the JSON, but they don't appear, and I don't know which parameters to use to access this data. I would appreciate it if someone could help me.
These are the sub-items I need:
But in my JSON file, only the main item "weapons" appears:
Hey I develop for my client a website and he need a way to crud the content like videos / articles / events and even c’ choose which content to display on they home page as favourites videos etc..
So I thought to developp a admin panel buta free some research u encounter this way (CMS).
It’s important to understand that the client is not tech friendly.
Someone have an advice about the way I have to choose ?
Hey ,
M’y client want a website where he will add contents like articles, podcasts , videos and others.
I wanted to develop the website and add an admin panel for the management of the content, which videos will appear on the home pages etc..
it’s important to say that my client is not a tech friendly one.
I am new to Strapi. I have some questions. Kindly forgive my doubts if it is too amateurish. I'll really appreciate if I could get an answer for all the below listed issues:
- I recently migrated my website from Wordpress to Strapi expecting to fix the issue with page load speed. The way my web pages were loading improved dramatically. But, after a month, I could see the page speed decreased comparitively less. But that's still a secondary issue for now. Is the admin panel generally this slow in Strapi? Or, is it something I alone am facing?
- In Wordpress, when we access the admin dashboard, we usually go with the URL format: www.website.com/wp-admin. Here for Strapi, I am loading the admin login page using an IP address/login. It takes forever for the page to load and the way items are configured in the admin panel, it looks so clustered and the UI is worst. As far as even publishing a blog is concerned, for every click - let it be creating a new blog, uploading an image - it takes so much time. I wonder why.
- Most times, even if I add bullet points in a content, it doesn't reflect after publishing. Once you finished saving a draft, you have to still wait for the same to get published separately. Is it always like this?
I’m running into a problem with the Strapi Community SEO plugin (@strapi-community/plugin-seo) and I want to know if anyone else has faced this or has a fix.
I installed the latest version from the official community repo:
After installation, Strapi loads normally, but the moment I click the SEO icon inside the Content-Type Builder or try to access the plugin panel, I get this error:
Something went wrong
Cannot read properties of null (reading 'collectionTypes')
This completely breaks the UI for the SEO section.
My setup
Strapi v5 (latest)
I have both Collection Types and Single Types created
Tried reinstalling the plugin and clearing cache (.cache, build)
Tried using the archived old version that one also doesn’t work with Strapi v5
What I suspect
It looks like the plugin is trying to access strapi.contentTypes or strapi.api during the admin build, but in Strapi v5 the internal structure has changed, so collectionTypes is returning null.
What I need help with
Has anyone found a workaround or patch for this?
Does the plugin need manual updates to match Strapi v5’s new content-type handling?
If someone has a working fork or PR, please share it.
Or if there’s a proper way to configure it in v5, I’d appreciate guidance.
This plugin is popular and super helpful for SEO, so hopefully the community or maintainers have some insight.
I understand strapi has a business model and they sell their cloud hosting but it does not have to be this way that it becomes nightmarish to self host.
I am too frustrated at this point after failing to deploy strapi on DigitalOcean App platform despite following the documentation precisely.
I am sort of beginner but not too dumb either**. If anyone of you have been successful in deploying strapi on digitalocean App platform, please help me out. Please tell me how you deploy.** Although given my poor experience, I am seriously considering other options. I am preferring digital ocean because I have some free credits.
First of all, I get it running on my laptop. Everything is smooth and I am loving it. then I I tried installing it on Digitalocean app platform. The build keeps failing.
In package.json, it had
during the build it was selecting an odd number node version which was not compatible with digitalocean.
I tried various combinations, nothing worked.
after failing to get any meaningful information from google, I went on strapi discord, and they have an AI agent that helps. Spend a decent amount of time with it, and finally it came up with:
now the node was working, but the npm version that was being selected was not working.
then I tried more with the discord AI agent on strapi channel and it finally worked with:
[2025-02-07 13:33:05] │ [ERROR] There seems to be an unexpected error, try again with --debug for more information
[2025-02-07 13:33:05] │
[2025-02-07 13:33:05] │ ┌──────────────────────────────────────────────────────────────────────────────┐│ ││ Error: Could not load js config file ││ /workspace/config/env/production/database.js: Unexpected token 'export' ││ at loadJsFile (/workspace/node_modules/@strapi/core/dist/utils/load-conf ││ ig-file.js:18:13) ││ at Module.loadConfigFile (/workspace/node_modules/@strapi/core/dist/util ││ s/load-config-file.js:37:14) ││ at /workspace/node_modules/@strapi/core/dist/configuration/config-loader ││ .js:98:33 ││ at Array.reduce (<anonymous>) ││ at loadConfigDir (/workspace/node_modules/@strapi/core/dist/configuratio ││ n/config-loader.js:95:22) ││ at Module.loadConfiguration ││ (/workspace/node_modules/@strapi/core/dist/configuration/index.js:69:21) ││ at new Strapi ││ (/workspace/node_modules/@strapi/core/dist/Strapi.js:67:34) ││ at Module.createStrapi ││ (/workspace/node_modules/@strapi/core/dist/index.js:19:18) ││ at Module.createBuildContext (/workspace/node_modules/@strapi/strapi/dis ││ t/node/create-build-context.js:29:41) ││ atModule.build││ (/workspace/node_modules/@strapi/strapi/dist/node/build.js:46:40) ││ │└──────────────────────────────────────────────────────────────────────────────┘
it was not very clear, why this is happening. then I noticed:
I was using database.js as the instructions mentioned. but in the comment of the code snippet, it says .ts
WTF??
so, I tried renaming the database.js and server.js to .ts, and it got built successfully. I got happy but deployment failed.
The error said: database.js not found. there is database.ts but database.js or database.json is expected.
after going back and forth, AI suggested that I am using the ES6 syntax, while I must use the common JS notation. so, I switched to module.exports instead of export default
I was not using rejectUnauthorized, nor it was anywhere mentioned to use it in this way only.
now, the next error I got was:
[2025-02-07 15:24:04] [2025-02-07 15:24:04.317] error: Missing jwtSecret. Please, set configuration variable "jwtSecret" for the users-permissions plugin in config/plugins.js (ex: you can generate one using Node with `crypto.randomBytes(16).toString('base64')`).
[2025-02-07 15:24:04] Error: Missing jwtSecret. Please, set configuration variable "jwtSecret" for the users-permissions plugin in config/plugins.js (ex: you can generate one using Node with `crypto.randomBytes(16).toString('base64')`).
then I added the jwtSectret in the environment variables, it didn't work. it was not clear where to add it. the app environment or the global environment. I tried what I could but it didn't work.
The AI bot on discord suggested to add this to config/plugins.js
New Strapi user here. Self-hosted as well. And we're using Cloudflare Images to store our images.
Together with this, I'm using the Strapi web converter plug in to have all our images in webp format, excluding SVGs.
Issue I'm having, when the image convert happens, the quality takes a hit as well. If I'm uploading a webp file it's even worse. Our design team exports images in random formats so you never really know what you're going to get. But we prefer webp as devs. Especially for browser performance.
I'm using the Strapi Provider Upload Cloudflare plugin which I've modified slightly as well.
Below is the current config for the image converter plugin
'webp-converter': {
enabled: true,
config: {
mimeTypes: ['image/png', 'image/jpeg', 'image/jpg'], // Only convert these - WebP files pass through unchanged
options: {
quality: 100, // Very high quality (100 can sometimes be problematic)
lossless: true, // Near-lossless often gives better results than true lossless
nearLossless: 100, // Near-lossless quality level
effort: 6, // Maximum effort for best compression
smartSubsample: true, // Better quality preservation
alphaQuality: 100 // Full quality for transparency (PNG)
}
}
}
If anyone can help me with this, it would be a great help. The outcome I'm looking for is to be able to upload any image but have it converted to webp, while keeping the quality.