Skill 01 · Storefront Best Practices
Subchapter 1.25
reference/medusa.mdMarkdown10 KBView on GitHub
Guide for connecting your storefront to Medusa backend using the Medusa JS SDK (opens in a new tab).
When to use this guide:
For general backend patterns, see reference/connecting-to-backend.md.
BEFORE writing code that calls Medusa SDK methods, follow the mandatory workflow from SKILL.md:
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.
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 (opens in a new tab)
The MCP server provides real-time method verification without leaving your IDE.
npm install @medusajs/js-sdk@latest @medusajs/types@latestBoth required: SDK provides functionality, types provide TypeScript support.
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.
IMPORTANT: Storefront Port Configuration
http://localhost:8000medusa-config.ts:
store_cors: process.env.STORE_CORS || "http://localhost:YOUR_PORT"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:
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:
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 discountsThe 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 (retrieval by ID does not require authentication; listing orders requires customer authentication)sdk.store.payment.* - Payment operationssdk.store.fulfillment.* - Shipping/fulfillment operationssdk.store.region.* - Region operationsTo find specific methods: Consult documentation (https://docs.medusajs.com/resources/js-sdk (opens in a new tab)) or use MCP server.
IMPORTANT: The patterns below show WHAT to do, not exact HOW. Always verify method names and signatures with MCP server or documentation before using.
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.
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):
Other cart updates (email, addresses, region, promo codes):
To implement: Query MCP server or documentation for exact cart method signatures: https://docs.medusajs.com/resources/references/js-sdk/store/cart (opens in a new tab)
High-level workflow:
To implement: Query MCP/docs for:
Resources:
reference/layouts/checkout.md for checkout flowHigh-level workflow:
To implement: Query MCP/docs for each step’s methods. Don’t guess method names.
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.
Critical for Medusa: Region determines currency, pricing, taxes, and available products.
Medusa requires region for:
region_id)High-level workflow:
selectedRegion.id for all cart and product operationsWhen user changes country:
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.mdSDK throws FetchError with:
status: HTTP status codestatusText: Error codemessage: Descriptive messagetry {
const data = await sdk.store.customer.retrieve()
} catch (error) {
const fetchError = error as FetchError
if (fetchError.statusText === "Unauthorized") {
redirect('/login')
}
}For custom API routes:
const data = await sdk.client.fetch(`/custom/endpoint`, {
method: "POST",
body: { /* ... */ },
})Source