What belongs in the CMS vs external systems
What belongs in the CMS vs external systems
TL;DR:
- If content teams create and maintain data through editorial workflows, it belongs in Contentstack; if it is system-generated, transactional, or changes at frequencies editorial workflows cannot match, it belongs elsewhere.
- Never store real-time pricing, inventory, user profiles, or session data in the CMS.
- Use the “hybrid composition” pattern: your frontend fetches editorial content from Contentstack and transactional data from external APIs, combining them at render time.
- Connect systems via a shared identifier (e.g., a shopify_handle field), not by copying data between them.
Every content platform eventually becomes a dumping ground if nobody draws clear boundaries around what goes in. Contentstack is a system of record for content — editorial text, images, structured data that content teams create and manage. It is not a database for every piece of information your application needs. Drawing the line between what belongs in the CMS and what belongs in external systems is one of the most consequential decisions you make as a developer on a composable architecture, because mistakes here create performance problems, editorial confusion, and integration fragility that compound over time.
The decision framework
The core question is simple: who creates and maintains this data?
If content teams — editors, marketers, documentation writers — create and edit the data through an editorial workflow, it belongs in Contentstack. If the data is system-generated, transactional, computed, or changes at a frequency that editorial workflows cannot accommodate, it belongs in an external system.
This is not a technical distinction about data formats. JSON can represent anything. The distinction is about ownership and lifecycle. Editorial content follows a publish workflow: someone drafts it, someone reviews it, someone approves it, and it gets published to an environment. Transactional data follows a completely different lifecycle: it is generated by systems, updated by automated processes, and consumed in real time.
Here is the framework applied to specific data types:
| Data type | Owner | Belongs in Contentstack? | Why |
|---|---|---|---|
| Product descriptions | Marketing team | Yes | Editorial content, reviewed and published |
| Product prices | Commerce system | No | Changes frequently, governed by business rules, not editorial decisions |
| Inventory levels | Warehouse/ERP | No | Changes in real time, not editorial |
| Blog articles | Content team | Yes | Core editorial content |
| User profiles | Authentication system | No | Transactional, user-generated |
| Order history | Commerce/ERP | No | Transactional, system-generated |
| Navigation menus | Content team | Yes | Structural content, editorially managed |
| Session data | Application server | No | Ephemeral, per-user, real-time |
| Promotional banners | Marketing team | Yes | Campaign content with publish schedules |
| Analytics data | Analytics platform | No | System-generated, aggregated |
| FAQ content | Content team | Yes | Editorial, versioned, localized |
| Application config | Engineering team | No | Deployment-specific, not editorial |
What belongs in Contentstack
Contentstack excels as the system of record for content that content teams own. This includes:
Marketing and campaign content: Landing pages, promotional banners, hero sections, call-to-action text, campaign-specific messaging. This content has a clear editorial lifecycle — it is drafted for a campaign, reviewed, published on a launch date, and often unpublished when the campaign ends. Contentstack's scheduling and release features support this workflow natively.
Product editorial content: Product descriptions, feature highlights, comparison tables, sizing guides, care instructions. This is the narrative layer that marketing teams write about products. It follows editorial workflows and benefits from localization, versioning, and approval processes.
Documentation and knowledge base content: FAQ entries, help articles, troubleshooting guides, onboarding content. Structured content types with fields for questions, answers, categories, and related articles map cleanly to Contentstack's content modeling capabilities.
Navigation and structural content: Header menus, footer links, sidebar navigation, breadcrumb structures. These are editorially managed and change infrequently. Storing them in Contentstack means content teams can update navigation without developer deployments.
Media and assets: Images, videos, PDFs, and other files that accompany editorial content. Contentstack's asset management includes a CDN, image transformation API, and folder organization — purpose-built for editorial media.
What does not belong in Contentstack
Equally important is being explicit about what should not be stored in the CMS:
Real-time pricing and inventory: Prices change based on promotions, geographic rules, currency conversion, and dynamic pricing algorithms. Inventory changes with every purchase. Neither of these follows an editorial workflow, and storing them in Contentstack means they are stale the moment an editor publishes them. These belong in a commerce platform like Shopify, commercetools, or your ERP system.
User accounts and profiles: User data is transactional and privacy-sensitive. It is created by users, not editors. It is governed by authentication systems, consent policies, and data retention regulations. Contentstack is not an identity provider and should not store personal user data.
Transaction and order data: Purchase history, shipping status, payment records, subscription state. These are generated by commerce and billing systems and have their own audit, compliance, and retention requirements.
Session and state data: Shopping carts, user preferences, authentication tokens, form progress. This data is ephemeral, per-user, and changes on every interaction. It belongs in session stores, cookies, or client-side state management.
Application configuration: Feature flags, A/B test parameters, API endpoints, environment-specific settings. These are deployment concerns managed by engineering, not editorial decisions. Use configuration services like LaunchDarkly, environment variables, or config files.
Logs and analytics: Server logs, user behavior data, conversion metrics. These are high-volume, append-only data streams that belong in analytics platforms and data warehouses.
The hybrid pattern: composition at render time
The real power of a composable architecture is that your frontend combines data from multiple systems at render time. Contentstack provides the editorial layer, and external APIs provide everything else. The frontend is the composition point.
Consider a product detail page for a retail company. The page needs:
- Product name, description, features, and images (editorial — from Contentstack)
- Current price and available discounts (transactional — from Shopify Storefront API)
- Real-time stock availability (transactional — from inventory API)
- Customer reviews and ratings (user-generated — from reviews platform like Bazaarvoice)
The frontend fetches from all four sources and assembles the complete page:
// Product page: composing data from multiple systems
async function getProductPageData(slug: string) {
// Editorial content from Contentstack
const query = stack.contentType("product").entry().query();
const cmsResult = await query.equalTo("url", `/products/${slug}`).find();
const editorial = cmsResult.entries[0];
// Price and variants from Shopify Storefront API
const shopifyResponse = await fetch(SHOPIFY_STOREFRONT_URL, {
method: "POST",
headers: {
"X-Shopify-Storefront-Access-Token": SHOPIFY_TOKEN,
"Content-Type": "application/json",
},
body: JSON.stringify({
query: `{
productByHandle(handle: "${slug}") {
variants(first: 10) {
edges { node { priceV2 { amount currencyCode } availableForSale } }
}
}
}`,
}),
});
const pricing = await shopifyResponse.json();
// Inventory from warehouse API
const inventoryResponse = await fetch(
`${INVENTORY_API}/products/${editorial.sku}/availability`
);
const inventory = await inventoryResponse.json();
return {
editorial, // name, description, images, features from CMS
pricing, // current prices from Shopify
inventory, // stock levels from warehouse
};
}In this pattern, each system is the authoritative source for its own data. The CMS does not try to replicate pricing, and the commerce platform does not try to store rich marketing descriptions. The frontend composes them into a unified experience.
A retail company example: drawing the boundaries
Consider a mid-size retailer running Contentstack as their CMS, Shopify as their commerce platform, and an ERP system for order management. Here is how they draw the boundaries:
In Contentstack:
- Product marketing pages (descriptions, lifestyle imagery, feature callouts)
- Category landing pages (curated collections, seasonal campaigns)
- Blog articles about product usage and styling
- Brand story pages
- Store locator content (store descriptions, hours, directions)
- Global navigation and footer
- Promotional banners and announcement bars
In Shopify:
- Product variants, SKUs, and pricing
- Inventory levels across warehouses
- Cart and checkout flow
- Customer accounts and order history
- Discount codes and promotion rules
- Shipping rates and tax calculations
In the ERP:
- Purchase orders and supplier management
- Warehouse operations and fulfillment
- Financial reporting and accounting
- Returns processing
The connection between systems is the product identifier. Contentstack entries for product content include a shopify_handle or sku field that links editorial content to the corresponding Shopify product. The frontend uses this identifier to fetch from both systems and compose the page.
{
"title": "Merino Wool Crew Neck Sweater",
"url": "/products/merino-crew-neck",
"shopify_handle": "merino-wool-crew-neck-sweater",
"description": "Crafted from 100% Australian merino wool...",
"features": [
{ "icon": "temperature", "text": "Temperature regulating" },
{ "icon": "wash", "text": "Machine washable" }
],
"size_guide": { ... },
"lifestyle_images": [ ... ],
"care_instructions": "..."
}The shopify_handle field is a simple text field in the content type schema. It does not create a live connection — it is a key that the frontend uses to correlate data from both systems. The editorial team fills it in once, and the frontend handles the runtime composition.
Common boundary mistakes
Common pitfall: Storing inventory or pricing as CMS fields means the data is stale the moment an editor publishes it — and flash sales or regional pricing cannot be managed without a full editorial review cycle.
Mistake 1: Storing inventory or pricing in Contentstack
This is the most frequent boundary violation. A team stores product prices as number fields in their Contentstack product content type. It works initially, but problems surface quickly: prices need to change for flash sales without going through content review, regional pricing requires duplicating entries instead of using commerce-native multi-currency support, and inventory counts are stale the moment they are published. The fix is to keep only editorial content in Contentstack and fetch dynamic data from the commerce platform at render time.
Mistake 2: Using the CMS as a configuration store
Some teams store application settings — API endpoints, feature flags, color theme values, redirect rules — as Contentstack entries. The CMS becomes a key-value store that engineering manages, cluttering the editorial interface with non-content entries. Editors see content types they do not understand and should not touch. Application configuration belongs in environment variables, configuration services, or code.
Mistake 3: Storing user-generated content in entries
Attempting to write user reviews, comments, or forum posts into Contentstack entries via the Content Management API treats the CMS as a transactional database. The CMA is not designed for high-frequency writes from anonymous users. It has rate limits, requires management tokens (not suitable for client-side calls), and lacks the access control model needed for user-generated content. Use purpose-built platforms for user-generated content and integrate them on the frontend.