medusa
Build headless e-commerce backends with Medusa.js. Use when a user asks to build an online store backend, create a headless e-commerce platform, set up product management and checkout, build a custom storefront, add payment processing to an e-commerce app, manage inventory and orders, or replace Shopify with an open-source alternative. Covers products, carts, orders, payments (Stripe), shipping, plugins, and Next.js storefront integration.
Usage
Getting Started
- Install the skill using the command above
- Open your AI coding agent (Claude Code, Codex, Gemini CLI, or Cursor)
- Reference the skill in your prompt
- The AI will use the skill's capabilities automatically
Example Prompts
- "Review the open pull requests and summarize what needs attention"
- "Generate a changelog from the last 20 commits on the main branch"
Documentation
Overview
Medusa is an open-source headless e-commerce engine (Node.js + PostgreSQL). It provides a complete backend for products, carts, orders, payments, shipping, and customers — exposed via REST and GraphQL APIs. Think of it as the open-source alternative to Shopify's backend, with full control over customization. This skill covers setup, product management, custom API routes, payment integration (Stripe), and connecting a Next.js storefront.
Instructions
Step 1: Project Setup
# Create new Medusa project
npx create-medusa-app@latest my-store
# This scaffolds: backend (Medusa server) + admin dashboard + storefront (Next.js)
# Or install backend only
npx create-medusa-app@latest my-store --skip-db --no-browser
cd my-store
# Configure database
# .env
DATABASE_URL=postgresql://user:pass@localhost:5432/medusa_store
REDIS_URL=redis://localhost:6379
# Run migrations and seed
npx medusa db:migrate
npx medusa seed --seed-file=data/seed.json
# Start development server
npx medusa develop
# API: http://localhost:9000
# Admin: http://localhost:7001
Step 2: Product Management
// src/api/routes/admin/custom-products.ts — Custom product creation endpoint
// Medusa's admin API handles CRUD, but custom routes extend it
import type { MedusaRequest, MedusaResponse } from "@medusajs/framework"
import { createProductsWorkflow } from "@medusajs/medusa/core-flows"
export async function POST(req: MedusaRequest, res: MedusaResponse) {
const { result } = await createProductsWorkflow(req.scope).run({
input: {
products: [{
title: req.body.title,
description: req.body.description,
status: "published",
options: [
{ title: "Size", values: ["S", "M", "L", "XL"] },
{ title: "Color", values: ["Black", "White", "Navy"] },
],
variants: [
{
title: "Small Black",
sku: "TSHIRT-S-BLK",
prices: [{ amount: 2999, currency_code: "usd" }],
options: { Size: "S", Color: "Black" },
manage_inventory: true,
inventory_quantity: 100,
},
],
images: [{ url: "https://example.com/product.jpg" }],
categories: [{ id: "cat_tshirts" }],
}],
},
})
res.json({ product: result })
}
# REST API examples — Product operations
# List products (public storefront API)
curl http://localhost:9000/store/products
# Get single product
curl http://localhost:9000/store/products/prod_01ABC
# Admin: create product
curl -X POST http://localhost:9000/admin/products \
-H 'Authorization: Bearer admin_token' \
-H 'Content-Type: application/json' \
-d '{"title": "Classic T-Shirt", "status": "published"}'
Step 3: Cart and Checkout Flow
// lib/storefront-client.ts — Client-side cart operations for Next.js storefront
// This is the typical checkout flow: create cart → add items → add shipping → complete
const API_URL = process.env.NEXT_PUBLIC_MEDUSA_URL || "http://localhost:9000"
export async function createCart() {
const res = await fetch(`${API_URL}/store/carts`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ region_id: "reg_us" }),
})
return res.json() // { cart: { id: "cart_01ABC", items: [], total: 0 } }
}
export async function addToCart(cartId: string, variantId: string, quantity: number) {
const res = await fetch(`${API_URL}/store/carts/${cartId}/line-items`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ variant_id: variantId, quantity }),
})
return res.json()
}
export async function completeCheckout(cartId: string) {
// Add customer email
await fetch(`${API_URL}/store/carts/${cartId}`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email: "customer@example.com" }),
})
// Select shipping option
await fetch(`${API_URL}/store/carts/${cartId}/shipping-methods`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ option_id: "so_standard" }),
})
// Initialize payment (Stripe)
await fetch(`${API_URL}/store/carts/${cartId}/payment-sessions`, {
method: "POST",
})
// Complete order
const res = await fetch(`${API_URL}/store/carts/${cartId}/complete`, {
method: "POST",
})
return res.json() // { type: "order", data: { id: "order_01ABC", ... } }
}
Step 4: Payment Integration (Stripe)
# Install Stripe payment plugin
npm install @medusajs/payment-stripe
// medusa-config.ts — Configure Stripe payment provider
import { defineConfig } from "@medusajs/framework"
export default defineConfig({
projectConfig: {
databaseUrl: process.env.DATABASE_URL,
redisUrl: process.env.REDIS_URL,
},
modules: [
{
resolve: "@medusajs/payment",
options: {
providers: [{
resolve: "@medusajs/payment-stripe",
options: {
apiKey: process.env.STRIPE_SECRET_KEY,
webhookSecret: process.env.STRIPE_WEBHOOK_SECRET,
},
}],
},
},
],
})
Step 5: Custom API Routes
// src/api/routes/store/custom-search.ts — Custom storefront endpoint
// Extend Medusa's API with business-specific logic
import type { MedusaRequest, MedusaResponse } from "@medusajs/framework"
export async function GET(req: MedusaRequest, res: MedusaResponse) {
const query = req.query.q as string
const productService = req.scope.resolve("product")
const [products, count] = await productService.listAndCount(
{ q: query, status: "published" },
{ take: 20, relations: ["variants", "images"] }
)
res.json({ products, count })
}
Step 6: Subscribers and Events
// src/subscribers/order-placed.ts — React to events (send email, update inventory)
import type { SubscriberArgs, SubscriberConfig } from "@medusajs/framework"
export default async function orderPlacedHandler({ event, container }: SubscriberArgs) {
/**
* Triggered when an order is placed. Use for:
* - Sending confirmation emails
* - Notifying warehouse
* - Updating external systems (ERP, CRM)
*/
const order = event.data
const notificationService = container.resolve("notification")
await notificationService.send("order-confirmation", {
to: order.email,
data: {
order_id: order.id,
items: order.items,
total: order.total,
},
})
}
export const config: SubscriberConfig = {
event: "order.placed",
}
Examples
Example 1: Launch a headless e-commerce store with Next.js frontend
User prompt: "I want to build an online clothing store. Open-source backend, custom Next.js frontend, Stripe payments. I need product variants (size, color), inventory tracking, and a proper checkout flow."
The agent will:
- Scaffold a Medusa project with
create-medusa-app. - Configure PostgreSQL, Redis, and Stripe payment plugin.
- Create product data model with size/color variants and inventory.
- Build Next.js storefront pages: product listing, product detail, cart, checkout.
- Set up order confirmation emails via subscribers.
Example 2: Migrate from Shopify to a self-hosted solution
User prompt: "We're paying $300/month for Shopify Plus. Migrate our 500 products and order history to a self-hosted Medusa backend."
The agent will:
- Export products and orders from Shopify via their Admin API.
- Transform the data to Medusa's format (handle variants, images, metadata).
- Write a migration script that imports products and historical orders.
- Set up equivalent payment and shipping configurations.
Guidelines
- Medusa requires PostgreSQL and Redis — both must be running before starting the server. Use Docker Compose to manage all three services together.
- The admin dashboard (port 7001) is separate from the storefront API (port 9000). Keep the admin behind authentication and network restrictions in production.
- Use Medusa's workflow system for complex operations (multi-step processes like checkout) — it provides automatic rollback on failure.
- For storefronts, use Medusa's official Next.js starter as a base rather than building from scratch — it handles cart state, checkout flow, and API integration.
- Medusa's plugin ecosystem covers payments (Stripe, PayPal), shipping (FedEx, DHL), notifications (SendGrid, Twilio), and search (Meilisearch, Algolia). Check the plugin registry before building custom integrations.
Information
- Version
- 1.0.0
- Author
- terminal-skills
- Category
- Development
- License
- Apache-2.0