dist/cursor/medusa-commerce/skills/medusa-api-routes/SKILL.md
Create custom Medusa v2 API routes — file-based routing, HTTP method exports, middleware configuration, Zod validators, authentication middleware, and additional-data pattern. Use when adding REST endpoints.
npx skillsauth add orcaqubits/agentic-commerce-claude-plugins medusa-api-routesInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Fetch live docs:
https://docs.medusajs.com/learn/fundamentals/api-routes for API route overviewsite:docs.medusajs.com custom API route file convention for file-based routingsite:docs.medusajs.com API route middleware for middleware configurationsite:docs.medusajs.com API route validation zod for request validationsite:docs.medusajs.com additional data API routes for the additional-data patternMedusa v2 uses a file-system router under src/api/:
| File Path | Resulting Endpoint |
|-----------|-------------------|
| src/api/store/custom/route.ts | GET/POST /store/custom |
| src/api/admin/custom/route.ts | GET/POST /admin/custom |
| src/api/store/custom/[id]/route.ts | GET/POST /store/custom/:id |
| src/api/custom/route.ts | GET/POST /custom (no auth prefix) |
route.ts (not index.ts)[param] folder syntaxstore/ prefix applies storefront authentication scopeadmin/ prefix applies admin authentication scopeEach route.ts exports named functions matching HTTP methods:
| Export Name | HTTP Method |
|-------------|-------------|
| GET | GET |
| POST | POST |
| PUT | PUT |
| PATCH | PATCH |
| DELETE | DELETE |
// src/api/store/custom/route.ts
// Fetch live docs for MedusaRequest/MedusaResponse types
import type { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
const service = req.scope.resolve("my-module")
res.json({ items: await service.listMyEntities() })
}
// src/api/store/custom/[id]/route.ts
// Fetch live docs for path parameter access
export const GET = async (req: MedusaRequest, res: MedusaResponse) => {
const item = await req.scope.resolve("my-module").retrieveMyEntity(req.params.id)
res.json({ item })
}
Medusa v2 uses Zod schemas for request body and query parameter validation:
| Validation Target | File |
|------------------|------|
| POST/PUT/PATCH body | validators.ts in same directory as route.ts |
| Query parameters | Same validators.ts file |
// src/api/store/custom/validators.ts
// Fetch live docs for Zod schema integration
import { z } from "zod"
export const PostStoreCustom = z.object({
name: z.string(),
description: z.string().optional(),
})
Validators are linked to routes via middleware configuration.
All middleware is configured in src/api/middlewares.ts:
// src/api/middlewares.ts — Fetch live docs for defineMiddlewares API
import { defineMiddlewares, validateAndTransformBody } from "@medusajs/framework/http"
import { PostStoreCustom } from "./store/custom/validators"
export default defineMiddlewares({
routes: [{ matcher: "/store/custom", method: "POST",
middlewares: [validateAndTransformBody(PostStoreCustom)] }],
})
| Utility | Purpose |
|---------|---------|
| validateAndTransformBody(schema) | Validate request body with Zod |
| validateAndTransformQuery(schema) | Validate query parameters |
| authenticate("customer", ["session", "bearer"]) | Require customer auth |
| authenticate("user", ["session", "bearer"]) | Require admin auth |
| Route Prefix | Default Auth | Custom Auth Override |
|-------------|-------------|---------------------|
| /admin/* | Admin user required | Can be loosened per route |
| /store/* | Optional customer auth | Can require auth per route |
| /custom/* | None | Must add explicitly |
// In middlewares.ts routes array
// Fetch live docs for authenticate() options
{
matcher: "/store/custom/me",
middlewares: [authenticate("customer", ["session", "bearer"])],
}
Medusa v2 supports passing extra data through built-in API routes to workflow hooks:
additional_dataadditionalDataValidator in middlewareadditional_dataThis allows extending core commerce flows (e.g., adding custom fields to product creation) without overriding API routes.
| Error Type | Recommended Approach |
|-----------|---------------------|
| Validation error | Handled automatically by Zod middleware (400) |
| Not found | Throw MedusaError with NOT_FOUND type |
| Unauthorized | Handled by auth middleware (401) |
| Business logic | Throw MedusaError with appropriate type |
| Unexpected | Let Medusa error handler return 500 |
validateAndTransformBodyreq.scope -- never import services directlyadditional_data pattern to extend core routes instead of overriding themauthenticate() middleware explicitly for routes requiring auth outside default scopes{ item } for single, { items, count, offset, limit } for listsFetch the Medusa API route documentation for exact file conventions, middleware utilities, and Zod integration patterns before implementing.
development
Build with Spree's headless Next.js storefront — the official `spree/storefront` repo (Next.js 16 App Router with Server Actions and Turbopack, React 19 Server Components, Tailwind CSS 4, TypeScript 5, `@spree/sdk`, Sentry), server-only auth (httpOnly JWT cookies + publishable key), MeiliSearch faceted catalog, one-page checkout with Apple/Google Pay/Klarna/Affirm/SEPA, multi-region market routing, GA4 + JSON-LD SEO, and Vercel/Docker deployment. Use when forking or customizing the storefront, or evaluating headless adoption.
tools
Build Spree extensions as Rails engines — gem scaffolding, `bin/rails g spree:extension`, mounting routes/migrations/assets, the modern `prepend` decorator pattern (`*_decorator.rb` with `self.prepended(base)`), generators (`spree:model_decorator`, `spree:controller_decorator`), the four customization surfaces in preference order (Events > Webhooks > Dependencies > Decorators), Spree::Dependencies for swapping service objects, gem release/versioning, and the deprecated Deface engine. Use when building a reusable Spree extension or adding non-trivial customization to an app.
development
Build with Spree's event bus and Webhooks 2.0 — `Spree::Events` publication, `Spree::Subscriber` DSL with `subscribes_to` and `on`, wildcard matching, lifecycle events (`{model}.created/.updated/.deleted` via `publishes_lifecycle_events`), the canonical event catalog (order.*, payment.*, shipment.*, product.*), Webhooks 2.0 endpoints, HMAC-SHA256 signing (`X-Spree-Webhook-Signature`), exponential-backoff retries, and Sidekiq job orchestration. Use when wiring event-driven business logic, building webhook consumers, or replacing ActiveSupport callback chains.
tools
Cross-cutting Spree development patterns — the customization preference hierarchy (Events > Webhooks > Dependencies > Decorators), `Spree::Dependencies` service-object swapping, the `_decorator.rb` + `prepend` + `self.prepended` idiom, idempotent subscribers and webhook receivers, multi-store scoping discipline, prefixed IDs, calculator polymorphism (shipping/promotion/tax share the base), service-object composition with `dry-monads` or simple results, why to avoid `class_eval` reopening and Deface, and Spree-on-Rails idioms (Hotwire/Turbo Stimulus, ActiveStorage, Action Cable, Sidekiq). Use when designing the architecture of a Spree extension or solving cross-cutting concerns.