Unchained vs
MedusaJS
A technical deep-dive comparing two open-source e‑commerce engines. GraphQL-first vs REST-first, MongoDB vs PostgreSQL, EUPL vs MIT — and the built-in features that set them apart.
Architecture at a Glance
Two open-source engines, two fundamentally different approaches to building e‑commerce backends.
Feature-by-Feature Comparison
Side-by-side breakdown of capabilities across core, built-in features, enterprise, and infrastructure.
| Feature | Unchained | MedusaJS |
|---|---|---|
| Core | ||
| License | EUPL (Copyleft) | MIT |
| Primary API | GraphQL-first | REST-first |
| Database | MongoDB | PostgreSQL |
| Language | Node.js + TypeScript | Node.js + TypeScript |
| Built-in Features | ||
| Subscriptions / Enrollments | ||
| Quotations / RFQ | ||
| Event Ticketing | ||
| Product Catalog | ||
| Order Management | ||
| Payment Integrations | ||
| Enterprise | ||
| B2B Features | Advanced | Basic |
| MCP / AI Integration | ||
| OCSF Audit Trail | ||
| WebAuthn / Passkeys | ||
| Infrastructure | ||
| Docker-native | ||
| Horizontal Scaling | Stateless | Stateless |
| NPM Packages | 28 independent | Monorepo modules |
GraphQL vs REST
Unchained exposes a GraphQL API by default, letting you fetch exactly the data you need in a single request. MedusaJS uses REST endpoints, often requiring multiple round-trips for the same data.
class="code-comment">// Single GraphQL query — products with variants, pricing & stock
const { data } = await client.query({
query: gql`
query Products($limit: Int) {
products(limit: $limit) {
_id
texts { title description }
simulatedPrice(currency: "CHF") {
price { amount currency }
}
catalogChildren {
_id
texts { title }
simulatedStocks { quantity }
}
}
}
`,
variables: { limit: 10 }
});class="code-comment">// Multiple REST calls required for the same data
const products = await fetch('/store/products?limit=10')
.then(r => r.json());
class="code-comment">// For each product, fetch variants separately
const variants = await Promise.all(
products.map(p =>
fetch(`/store/products/${p.id}/variants`)
.then(r => r.json())
)
);
class="code-comment">// Prices require another endpoint
const prices = await fetch('/store/pricing/calculate', {
method: 'POST',
body: JSON.stringify({
product_ids: products.map(p => p.id),
currency_code: 'chf'
})
}).then(r => r.json());
class="code-comment">// Stock levels from inventory module
const stock = await fetch('/admin/inventory-items')
.then(r => r.json());Type Safety
GraphQL schemas provide compile-time type checking and auto-generated TypeScript types for every query.
Fewer Round-Trips
Fetch products, variants, prices, and stock in one request instead of chaining multiple REST calls.
Introspection
The schema is self-documenting. Tools like GraphQL Playground let you explore the entire API interactively.
Database Architecture
Different storage engines reflect different trade-offs: schema flexibility vs relational integrity.
MongoDB (Unchained)
- Flexible document schemas — embed variants, prices, and metadata directly inside product documents
- Schema-less evolution — add fields without migrations
- Horizontal scaling with native sharding
- PostgreSQL compatible via FerretDB when relational guarantees are needed
PostgreSQL (MedusaJS)
- ACID transactions for every write
- Normalized relational model with strong referential integrity
- Mature ecosystem of extensions (PostGIS, pg_trgm, etc.)
- JOIN-based queries across orders, products, and customers
Plugin & Module System
Both platforms are extensible, but they take different architectural approaches to customization.
Unchained: Adapter-Based Plugins
Unchained uses an adapter pattern where each concern (payments, delivery, messaging, warehousing) has a clearly defined interface. Swap providers by registering a different adapter — no core changes required. Each of the 28 NPM packages can be installed independently.
MedusaJS: Service/Repository Pattern
MedusaJS uses a module-based architecture with services, repositories, and subscribers. Modules are organized in a monorepo and extend the core through dependency injection and lifecycle hooks.
import { startPlatform } from '@unchainedshop/platform';
import { stripePayments } from '@unchainedshop/plugins';
await startPlatform({
modules: {
class="code-comment">// 28 independent NPM packages
payments: {
adapters: [stripePayments],
},
delivery: {
adapters: [postDelivery],
},
messaging: {
adapters: [sendgridMessaging],
},
warehousing: {
adapters: [customWarehouse],
},
},
});Built-in Enterprise Features
Features that Unchained ships natively — no plugins, no third-party services, no extra cost.
Subscriptions & Enrollments
Recurring billing, membership management, and enrollment workflows built into the core engine.
Quotations & RFQ
B2B negotiation workflows with request-for-quote, approval chains, and custom pricing per customer.
Event Ticketing
Apple Wallet and Google Wallet passes, QR code generation, and check-in management out of the box.
MCP Server
Native AI assistant integration via the Model Context Protocol. Let AI agents browse products, manage carts, and place orders.
OCSF Audit Trail
Compliance-grade logging following the Open Cybersecurity Schema Framework for full operational transparency.
WebAuthn / Passkeys
Passwordless authentication with biometric and hardware key support. FIDO2-compliant and phishing-resistant.
Ready to Build with Unchained?
Start building your next e‑commerce project on a GraphQL-first, enterprise-ready platform licensed under the EUPL.