From medusa-dev
Guides Medusa backend development for custom modules, API routes, workflows, data models, module links, and business logic with architecture patterns, best practices, and rules.
npx claudepluginhub adaptive-machines/medusa-agent-skills --plugin medusa-devThis skill uses the workspace's default tool permissions.
Comprehensive backend development guide for Medusa applications. Contains patterns across 6 categories covering architecture, type safety, business logic placement, and common pitfalls.
reference/api-routes.mdreference/authentication.mdreference/custom-modules.mdreference/data-models.mdreference/error-handling.mdreference/frontend-integration.mdreference/module-links.mdreference/querying-data.mdreference/scheduled-jobs.mdreference/subscribers-and-events.mdreference/troubleshooting.mdreference/workflow-hooks.mdreference/workflows.mdCreates isolated Git worktrees for feature branches with prioritized directory selection, gitignore safety checks, auto project setup for Node/Python/Rust/Go, and baseline verification.
Executes implementation plans in current session by dispatching fresh subagents per independent task, with two-stage reviews: spec compliance then code quality.
Dispatches parallel agents to independently tackle 2+ tasks like separate test failures or subsystems without shared state or dependencies.
Comprehensive backend development guide for Medusa applications. Contains patterns across 6 categories covering architecture, type safety, business logic placement, and common pitfalls.
Load this skill for ANY backend development task, including:
Also load these skills when:
The quick reference below is NOT sufficient for implementation. You MUST load relevant reference files before writing code for that component.
Load these references based on what you're implementing:
reference/custom-modules.md firstreference/workflows.md firstreference/api-routes.md firstreference/module-links.md firstreference/querying-data.md firstreference/authentication.md firstMinimum requirement: Load at least 1-2 reference files relevant to your specific task before implementing.
ALWAYS follow this flow - never bypass layers:
Module (data models + CRUD operations)
↓ used by
Workflow (business logic + mutations with rollback)
↓ executed by
API Route (HTTP interface, validation middleware)
↓ called by
Frontend (admin dashboard/storefront via SDK)
Key conventions:
query.graph() for cross-module data retrievalquery.index() (Index Module) for filtering across separate modules with links| Priority | Category | Impact | Prefix |
|---|---|---|---|
| 1 | Architecture Violations | CRITICAL | arch- |
| 2 | Type Safety | CRITICAL | type- |
| 3 | Business Logic Placement | HIGH | logic- |
| 4 | Import & Code Organization | HIGH | import- |
| 5 | Data Access Patterns | MEDIUM (includes CRITICAL price rule) | data- |
| 6 | File Organization | MEDIUM | file- |
arch-workflow-required - Use workflows for ALL mutations, never call module services from routesarch-layer-bypass - Never bypass layers (route → service without workflow)arch-http-methods - Use only GET, POST, DELETE (never PUT/PATCH)arch-module-isolation - Use module links, not direct cross-module service callsarch-query-config-fields - Don't set explicit fields when using req.queryConfigtype-request-schema - Pass Zod inferred type to MedusaRequest<T> when using req.validatedBodytype-authenticated-request - Use AuthenticatedMedusaRequest for protected routes (not MedusaRequest)type-export-schema - Export both Zod schema AND inferred type from middlewarestype-linkable-auto - Never add .linkable() to data models (automatically added)type-module-name-camelcase - Module names MUST be camelCase, never use dashes (causes runtime errors)logic-workflow-validation - Put business validation in workflow steps, not API routeslogic-ownership-checks - Validate ownership/permissions in workflows, not routeslogic-module-service - Keep modules simple (CRUD only), put logic in workflowsimport-top-level - Import workflows/modules at file top, never use await import() in route bodyimport-static-only - Use static imports for all dependenciesimport-no-dynamic-routes - Dynamic imports add overhead and break type checkingdata-price-format - CRITICAL: Prices are stored as-is in Medusa (49.99 stored as 49.99, NOT in cents). Never multiply by 100 when saving or divide by 100 when displayingdata-query-method - Use query.graph() for retrieving data; use query.index() (Index Module) for filtering across linked modulesdata-query-graph - Use query.graph() for cross-module queries with dot notation (without cross-module filtering)data-query-index - Use query.index() when filtering by properties of linked data models in separate modulesdata-list-and-count - Use listAndCount for single-module paginated queriesdata-linked-filtering - query.graph() can't filter by linked module fields - use query.index() or query from that entity directlydata-no-js-filter - Don't use JavaScript .filter() on linked data - use database filters (query.index() or query the entity)data-same-module-ok - Can filter by same-module relations with query.graph() (e.g., product.variants)data-auth-middleware - Trust authenticate middleware, don't manually check req.auth_contextfile-workflow-steps - Recommended: Create steps in src/workflows/steps/[name].tsfile-workflow-composition - Composition functions in src/workflows/[name].tsfile-middleware-exports - Export schemas and types from middleware filesfile-links-directory - Define module links in src/links/[name].tsThe workflow function has critical constraints:
// ✅ CORRECT
const myWorkflow = createWorkflow(
"name",
function (input) { // Regular function, not async, not arrow
const result = myStep(input) // No await
return new WorkflowResponse(result)
}
)
// ❌ WRONG
const myWorkflow = createWorkflow(
"name",
async (input) => { // ❌ No async, no arrow functions
const result = await myStep(input) // ❌ No await
if (input.condition) { /* ... */ } // ❌ No conditionals
return new WorkflowResponse(result)
}
)
Constraints:
function)when())transform())transform()).config({ name: "unique-name" }) to avoid conflictsBefore implementing, verify you're NOT doing these:
Architecture:
fields explicitly with req.queryConfigType Safety:
MedusaRequest<SchemaType> type argumentMedusaRequest instead of AuthenticatedMedusaRequest for protected routes.linkable() to data modelsBusiness Logic:
req.auth_context?.actor_id when middleware already appliedImports:
await import() in route handler bodiesData Access:
query.graph() (use query.index() or query from other side instead).filter() on linked data (use query.index() or query the linked entity directly)query.graph() for cross-module data retrievalquery.graph() when you need to filter across separate modules (use query.index() instead)CRITICAL: Always run the build command after completing implementation to catch type errors and runtime issues.
Detect the package manager and run the appropriate command:
npm run build # or pnpm build / yarn build
If the build fails:
Common build errors:
MedusaRequest<T> type argument)After successfully implementing a feature, always provide these next steps to the user:
If the server isn't already running, start it:
npm run dev # or pnpm dev / yarn dev
Open your browser and navigate to:
Log in with your admin credentials to test any admin-related features.
If you implemented custom API routes, list them for the user to test:
Admin Routes (require authentication):
POST http://localhost:9000/admin/[your-route] - Description of what it doesGET http://localhost:9000/admin/[your-route] - Description of what it doesStore Routes (public or customer-authenticated):
POST http://localhost:9000/store/[your-route] - Description of what it doesGET http://localhost:9000/store/[your-route] - Description of what it doesTesting with cURL example:
# Admin route (requires authentication)
curl -X POST http://localhost:9000/admin/reviews/123/approve \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_TOKEN" \
--cookie "connect.sid=YOUR_SESSION_COOKIE"
# Store route
curl -X POST http://localhost:9000/store/reviews \
-H "Content-Type: application/json" \
-d '{"product_id": "prod_123", "rating": 5, "comment": "Great product!"}'
Depending on what was implemented, mention:
Always present next steps in a clear, actionable format after implementation:
## Implementation Complete
The [feature name] has been successfully implemented. Here's how to test it:
### Start the Development Server
[server start command based on package manager]
### Access the Admin Dashboard
Open http://localhost:9000/app in your browser
### Test the API Routes
I've added the following routes:
**Admin Routes:**
- POST /admin/[route] - [description]
- GET /admin/[route] - [description]
**Store Routes:**
- POST /store/[route] - [description]
### What to Test
1. [Specific test case 1]
2. [Specific test case 2]
3. [Specific test case 3]
For detailed patterns and examples, load reference files:
reference/custom-modules.md - Creating modules with data models
reference/workflows.md - Workflow creation and step patterns
reference/api-routes.md - API route structure and validation
reference/module-links.md - Linking entities across modules
reference/querying-data.md - Query patterns and filtering rules
reference/authentication.md - Protecting routes and accessing users
reference/error-handling.md - MedusaError types and patterns
reference/scheduled-jobs.md - Cron jobs and periodic tasks
reference/subscribers-and-events.md - Event handling
reference/troubleshooting.md - Common errors and solutions
Each reference file contains:
⚠️ CRITICAL: This skill should be consulted FIRST for planning and implementation.
Use this skill for (PRIMARY SOURCE):
Use MedusaDocs MCP server for (SECONDARY SOURCE):
Why skills come first:
⚠️ CRITICAL: Frontend applications MUST use the Medusa JS SDK for ALL API requests
When building features that span backend and frontend:
For Admin Dashboard:
building-admin-dashboard-customizations skillsdk.admin.product.list())sdk.client.fetch("/admin/my-route")For Storefronts:
building-storefronts skillsdk.store.product.list())sdk.client.fetch("/store/my-route")Why the SDK is required:
x-publishable-api-key headerAuthorization and session headersSee respective frontend skills for complete integration patterns.