DEV Community

Cover image for I Like Strapi for Ecommerce. I Still Wouldn't Start With Strapi.
Nathan Allen
Nathan Allen

Posted on

I Like Strapi for Ecommerce. I Still Wouldn't Start With Strapi.

I wouldn't start with Strapi.

I wouldn't start with Sanity, Payload, or any other CMS either. The first decision in an ecommerce stack isn't the CMS. It's simpler than that. Which system owns each piece of data?

Teams skip that question because choosing a tool feels like progress. Content types get modeled. Pages get built. Then two systems quietly start storing the same price.

Ownership Comes Before the Tool

Before I look at any CMS, I list what the business actually stores. Products and variants. Prices and promotions. Inventory. Cart, checkout, and orders. Editorial content. Search indexes. Customer data. Anything the ERP or PIM already holds.

Then I ask three questions about each item. Who writes it? Who reads it? What should the storefront do when the owner is down?

The last question matters most. It's easy to say "the commerce backend owns prices." It's harder to decide what the page shows when that backend times out.

What Overlapping Ownership Looks Like

*Here's a stack I'd call reasonable:
*

Storefront (Next.js)
├─ Strapi → editorial content
├─ Medusa → products, prices, carts, orders
├─ ERP → warehouse stock
└─ Search → indexed catalog

Each box has one job. Strapi holds guides, campaigns, and FAQs. Medusa runs commerce. The ERP owns stock. Search owns discovery.

Now add one shortcut. A marketer wants a sale price inside a campaign banner. Someone adds a price field to the Strapi content type. Both Strapi and Medusa can now hold an editable price.

That's where the trouble starts. A sync job gets written. It runs late, or it fails quietly. Say the banner shows 49 and the cart shows 59. No CMS feature fixes that. It's an ownership bug.

The Architecture Mistake I See Most Often

The mistake is modeling content before deciding ownership. It feels harmless. A "Product Highlight" type with name, price, stock status, and image looks useful on day one.

Later, those fields drift. Names change in the commerce backend. Stock changes in the ERP. Editors fix what they can see, and nobody trusts the data anymore.

Now I write a one-page ownership table first. Every field gets one owner. If the CMS doesn't own a field, it doesn't get a copy of it. It gets a reference.

Store References, Not Copies

A reference is usually a product handle or ID. The CMS stores which product, not what the product is.

`// Editorial content comes from Strapi
const campaign = await strapi.getCampaign(slug);

// Product facts come from the commerce backend
const products = await commerce.getProducts(campaign.productHandles);`

This keeps one source of truth. But it has a cost. Every campaign page now needs two requests. Previews need commerce data too. If the commerce API is slow, the campaign page is slow.

So I decide the failure behavior up front. If products don't load, the page should still show the editorial content. The product block can hide or fall back. A buying guide shouldn't return a 500 because a price service had a bad minute.

I'd rather accept two requests than maintain two prices. But it's a tradeoff, not a free win.

Webhooks, Caching, and Stale Content

Once content and commerce live apart, freshness becomes your job. Strapi can send webhooks on events like entry.create, entry.update, entry.publish, and entry.unpublish. For most events, the payload contains the full entry with its relations and components.

A common pattern is to call your frontend when an editor publishes. The frontend then revalidates the affected page. In Next.js, that's on-demand revalidation.

Two details matter to me. First, secure the endpoint. Strapi lets you configure webhook headers, so verify them. Second, don't treat a webhook as a guarantee. I'd add a scheduled reconcile job, so one missed event doesn't leave a page stale for days.

Preview needs thought too. Editors want to see drafts. The preview route has to read draft content from Strapi and still fetch live commerce data. Test that path early. In my experience it breaks more easily than the published path.

Where Strapi Earns Its Place

I like Strapi when content needs real structure. Components and Dynamic Zones let editors assemble pages from approved blocks. REST and GraphQL let each channel request what it needs. Backend customization is real, and you can self-host it.

Next to Medusa, the split is clean. Medusa handles products, variants, pricing, carts, and orders through its commerce modules. Strapi handles guides, campaigns, FAQs, and reusable blocks. Different problems, different systems.

But using both increases what you own. That's two deployments, two upgrade paths, two permission models, and one more integration to monitor. Strapi 5 also changed core APIs, so I check version support before depending on any community plugin. If you want a closer look at the implementation side, this page on Strapi development services covers content models, integrations, and upgrades.

When I Wouldn't Add Strapi

The commerce platform already covers your content needs
Nobody owns hosting, upgrades, and security
The team wants fully managed infrastructure
Another CMS fits your editor workflow better
The design needs the CMS to duplicate operational data

The last one is a design problem, not a tooling problem. If the plan requires copying prices or stock into the CMS, I'd fix the plan before choosing software.

What I Ask Before Installing Anything

Who owns each field? What happens when that owner is unavailable? How does a content change reach the storefront? Who patches the CMS? Who owns the integration between systems?

If I can't answer those in plain language, the CMS isn't the next step. A missing answer usually means two systems will fight over the same data later.

The Principle I Keep

I still like Strapi. I just don't start there anymore.

I start with one page that says who owns what. Once that page is honest, the CMS choice gets smaller and calmer. Sometimes the answer is Strapi. Sometimes it's something simpler. Either way, the stack has fewer arguments waiting to happen.

Top comments (2)

Collapse
 
strapijs profile image
Strapi •

This is thoughtful architecture thinking. You're right: data ownership conflicts emerge when multiple systems hold copies of the same facts, and your solution (one owner per field, store references not copies) is sound.

A couple points on Strapi's design:

Webhooks: You're correct that they're your coordination layer. Strapi 5 webhooks now always send the full entry with all relations and components. We also removed the webhooks.populateRelations option. That means your integration logic has to be explicit about what you pull and ignore, which actually makes ownership boundaries clearer.

Components and dynamic zones: Exactly what you'd build those reusable blocks with. That constraint prevents the sync bugs you're describing.

Your closing principle is spot-on: the CMS choice gets simpler once ownership is honest. Sometimes that's Strapi. Sometimes it's something simpler.

By the way, Strapi now has an MCP server if you're looking to integrate it into your workflows: docs.strapi.io/cms/features/strapi...

Collapse
 
nathan_allen profile image
Nathan Allen •

Good points, especially on the webhook changes in Strapi 5. I hadn’t looked into the MCP server yet, but it’s on my list now. Thanks for sharing!