9.9 KiB
Medusa Backend Integration
Contents
- Overview
- Installation
- SDK Setup
- Vite Configuration
- TypeScript Types
- Price Display
- SDK Organization
- Critical Medusa Patterns
- Region State Management
Overview
Guide for connecting your storefront to Medusa backend using the Medusa JS SDK.
When to use this guide:
- Building a storefront with Medusa backend
- Need to integrate Medusa SDK properly
- Working with multi-region stores
- Handling Medusa-specific pricing and regions
For general backend patterns, see reference/connecting-to-backend.md.
⚠️ CRITICAL: Follow the 5-Step Verification Workflow
BEFORE writing code that calls Medusa SDK methods, follow the mandatory workflow from SKILL.md:
- PAUSE - Don't write code yet
- QUERY MCP server or docs (https://docs.medusajs.com/resources/js-sdk) for exact method
- VERIFY with user what you found
- WRITE code using verified method
- CHECK for TypeScript errors - Type errors mean wrong method name or parameters
If you see TypeScript errors on SDK methods, you used incorrect methods. Go back to Step 2 and verify again.
This file shows PATTERNS (what to do), not exact methods (how to do it). Always verify method names with MCP/docs before use.
💡 RECOMMENDED: Set Up Medusa MCP Server
If the Medusa MCP server is not installed, strongly recommend setting it up.
Setup instructions: add HTTP MCP server with URL https://docs.medusajs.com/mcp
The MCP server provides real-time method verification without leaving your IDE.
Installation
npm install @medusajs/js-sdk@latest @medusajs/types@latest
Both required: SDK provides functionality, types provide TypeScript support.
SDK Setup
import Medusa from "@medusajs/js-sdk"
export const sdk = new Medusa({
baseUrl: process.env.NEXT_PUBLIC_MEDUSA_BACKEND_URL || "http://localhost:9000",
debug: process.env.NODE_ENV === "development",
publishableKey: process.env.NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY,
})
CRITICAL: Always set publishableKey.
- Required for multi-region stores to get correct pricing
- Required for accessing products with regional prices
- Without it, product queries may fail or return incorrect prices
- Get publishable key from Medusa admin dashboard under Settings → Publishable API Keys
IMPORTANT: Storefront Port Configuration
- Run storefront at port 8000 to avoid CORS errors
- Medusa backend's default CORS configuration expects storefront at
http://localhost:8000 - If using different port, configure CORS in Medusa backend's
medusa-config.ts:store_cors: process.env.STORE_CORS || "http://localhost:YOUR_PORT" - Common framework defaults:
- Next.js: Port 3000 (needs CORS config update)
- TanStack Start: Port 3000 (needs CORS config update)
- Vite: Port 5173 (needs CORS config update)
- Recommended: Use port 8000 to avoid configuration changes
Vite Configuration (TanStack Start, Vite Projects)
IMPORTANT: For Vite-based projects, configure SSR externals.
Add this to your vite.config.ts:
export default defineConfig({
// ... other config
ssr: {
noExternal: ['@medusajs/js-sdk'],
},
})
Why this is needed:
- Medusa JS SDK must be processed by Vite during SSR
- Without this config, SDK calls will fail during server-side rendering
- Applies to TanStack Start, vanilla Vite, and other Vite-based frameworks
TypeScript Types
IMPORTANT: Always use @medusajs/types - never define custom types.
import type {
StoreProduct,
StoreCart,
StoreCartLineItem,
StoreRegion,
StoreProductCategory,
StoreCustomer,
StoreOrder
} from "@medusajs/types"
Why use official types:
- Complete and accurate type definitions
- Updated with each Medusa release
- Includes all entity relationships and fields
- Prevents type mismatches with API responses
Price Display
CRITICAL: Medusa prices are stored as-is - DO NOT divide by 100.
Unlike Stripe (where amounts are in cents), Medusa stores prices in their display value.
// ❌ WRONG - Dividing by 100
<div>${product.variants[0].prices[0].amount / 100}</div>
// ✅ CORRECT - Display as-is
<div>${product.variants[0].prices[0].amount}</div>
Correct price formatting:
const formatPrice = (amount: number, currencyCode: string) => {
return new Intl.NumberFormat('en-US', {
style: 'currency',
currency: currencyCode,
}).format(amount)
}
Price fields to use:
variant.calculated_price.calculated_amount- Final price including promotionsvariant.calculated_price.original_amount- Original price before discounts- Both are already in display format - no conversion needed
SDK Organization
The Medusa SDK is organized by resources:
sdk.store.product.*- Product operationssdk.store.cart.*- Cart operationssdk.store.category.*- Category operationssdk.store.customer.*- Customer operations (authenticated)sdk.store.order.*- Order operations (authenticated)sdk.store.payment.*- Payment operationssdk.store.fulfillment.*- Shipping/fulfillment operationssdk.store.region.*- Region operations
To find specific methods: Consult documentation (https://docs.medusajs.com/resources/js-sdk) or use MCP server.
Critical Medusa Patterns
IMPORTANT: The patterns below show WHAT to do, not exact HOW. Always verify method names and signatures with MCP server or documentation before using.
1. Always Pass region_id for Products
Pattern: Product queries require region_id parameter for correct pricing.
Why: Without region_id, calculated_price will be missing or incorrect.
To implement: Query MCP/docs for product listing and retrieval methods. Pass region_id: selectedRegion.id as parameter.
2. Cart Updates Pattern
Pattern: Line items have dedicated methods (create, update, delete). Other cart properties use a generic update method.
Line item operations (verify exact method names with MCP/docs):
- Add item to cart
- Update item quantity
- Remove item from cart
Other cart updates (email, addresses, region, promo codes):
- Use cart's generic update method
To implement: Query MCP server or documentation for exact cart method signatures: https://docs.medusajs.com/resources/references/js-sdk/store/cart
3. Payment Flow Pattern
High-level workflow:
- Query available payment providers for the cart's region
- User selects payment method
- Initialize payment session for selected provider
- Render provider-specific UI (Stripe Elements, etc.)
- Complete payment through provider
To implement: Query MCP/docs for:
- Payment provider listing method
- Payment session initialization method
- Payment completion method
Resources:
- MCP server (if installed)
- Medusa payment docs: https://docs.medusajs.com/resources/references/js-sdk/store/payment
reference/layouts/checkout.mdfor checkout flow
4. Checkout Flow Pattern
High-level workflow:
- Collect shipping address
- Query available shipping options for cart
- User selects shipping method
- Collect payment information
- Initialize payment session
- Complete/place order
To implement: Query MCP/docs for each step's methods. Don't guess method names.
5. Category Fetching
Pattern: Fetch categories from sdk.store.category.* resource.
To implement: Query MCP/docs for category listing method. See reference/components/navbar.md for usage patterns.
Region State Management
Critical for Medusa: Region determines currency, pricing, taxes, and available products.
Why Region Context Matters
Medusa requires region for:
- Creating carts (must pass
region_id) - Retrieving products with correct prices
- Determining currency and tax calculations
- Filtering available payment and shipping methods
Implementation Approach
High-level workflow:
- Fetch available regions on app load (query MCP/docs for region listing method)
- Detect user's country (IP, browser locale, or user selection)
- Find region containing that country
- Store selected region globally (React Context, Zustand, etc.)
- Use
selectedRegion.idfor all cart and product operations
When user changes country:
- Find new region containing the country
- Update cart with new region_id (query MCP/docs for cart update method)
- Store selection in localStorage for persistence
To implement: Query MCP server or docs for exact region and cart methods. Don't copy example code without verification.
For detailed region implementation with code examples, see:
reference/components/country-selector.md- Medusa MCP server (if installed)
- Medusa docs: https://docs.medusajs.com/resources/storefront-development/regions/context
Error Handling
SDK throws FetchError with:
status: HTTP status codestatusText: Error codemessage: Descriptive message
try {
const data = await sdk.store.customer.retrieve()
} catch (error) {
const fetchError = error as FetchError
if (fetchError.statusText === "Unauthorized") {
redirect('/login')
}
}
Custom Endpoints
For custom API routes:
const data = await sdk.client.fetch(`/custom/endpoint`, {
method: "POST",
body: { /* ... */ },
})
Resources
- Medusa JS SDK docs: https://docs.medusajs.com/resources/js-sdk
- Storefront development: https://docs.medusajs.com/resources/storefront-development
- Checkout flow: https://docs.medusajs.com/resources/storefront-development/checkout
- Region context: https://docs.medusajs.com/resources/storefront-development/regions/context
- Use Medusa MCP server if available for real-time method lookup