Initial commit
This commit is contained in:
307
.agents/skills/storefront-best-practices/reference/medusa.md
Normal file
307
.agents/skills/storefront-best-practices/reference/medusa.md
Normal file
@@ -0,0 +1,307 @@
|
||||
# Medusa Backend Integration
|
||||
|
||||
## Contents
|
||||
|
||||
- [Overview](#overview)
|
||||
- [Installation](#installation)
|
||||
- [SDK Setup](#sdk-setup)
|
||||
- [Vite Configuration](#vite-configuration-tanstack-start-vite-projects)
|
||||
- [TypeScript Types](#typescript-types)
|
||||
- [Price Display](#price-display)
|
||||
- [SDK Organization](#sdk-organization)
|
||||
- [Critical Medusa Patterns](#critical-medusa-patterns)
|
||||
- [Region State Management](#region-state-management)
|
||||
|
||||
## Overview
|
||||
|
||||
Guide for connecting your storefront to Medusa backend using the [Medusa JS SDK](https://docs.medusajs.com/resources/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:
|
||||
|
||||
1. **PAUSE** - Don't write code yet
|
||||
2. **QUERY** MCP server or docs (https://docs.medusajs.com/resources/js-sdk) for exact method
|
||||
3. **VERIFY** with user what you found
|
||||
4. **WRITE** code using verified method
|
||||
5. **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
|
||||
|
||||
```bash
|
||||
npm install @medusajs/js-sdk@latest @medusajs/types@latest
|
||||
```
|
||||
|
||||
Both required: SDK provides functionality, types provide TypeScript support.
|
||||
|
||||
## SDK Setup
|
||||
|
||||
```typescript
|
||||
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`:
|
||||
```typescript
|
||||
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`:
|
||||
|
||||
```typescript
|
||||
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.**
|
||||
|
||||
```typescript
|
||||
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.
|
||||
|
||||
```typescript
|
||||
// ❌ 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:**
|
||||
```typescript
|
||||
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 promotions
|
||||
- `variant.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 operations
|
||||
- `sdk.store.cart.*` - Cart operations
|
||||
- `sdk.store.category.*` - Category operations
|
||||
- `sdk.store.customer.*` - Customer operations (authenticated)
|
||||
- `sdk.store.order.*` - Order operations (authenticated)
|
||||
- `sdk.store.payment.*` - Payment operations
|
||||
- `sdk.store.fulfillment.*` - Shipping/fulfillment operations
|
||||
- `sdk.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:**
|
||||
1. Query available payment providers for the cart's region
|
||||
2. User selects payment method
|
||||
3. Initialize payment session for selected provider
|
||||
4. Render provider-specific UI (Stripe Elements, etc.)
|
||||
5. 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.md` for checkout flow
|
||||
|
||||
### 4. Checkout Flow Pattern
|
||||
|
||||
**High-level workflow:**
|
||||
1. Collect shipping address
|
||||
2. Query available shipping options for cart
|
||||
3. User selects shipping method
|
||||
4. Collect payment information
|
||||
5. Initialize payment session
|
||||
6. 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:**
|
||||
1. Fetch available regions on app load (query MCP/docs for region listing method)
|
||||
2. Detect user's country (IP, browser locale, or user selection)
|
||||
3. Find region containing that country
|
||||
4. Store selected region globally (React Context, Zustand, etc.)
|
||||
5. Use `selectedRegion.id` for 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 code
|
||||
- `statusText`: Error code
|
||||
- `message`: Descriptive message
|
||||
|
||||
```typescript
|
||||
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:
|
||||
|
||||
```typescript
|
||||
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
|
||||
Reference in New Issue
Block a user