Medusa v2 Platform Guide: Commerce Modules, Customization & Deployment
Reference document compiled from official Medusa v2 documentation. Covers commerce modules, custom module creation, storefront integration, admin customization, auth, file handling, and deployment.
1. Commerce Modules Overview
Commerce Modules are Medusa’s built-in modules providing core commerce logic for domains like Products, Orders, Customers, Fulfillment, and more. They form the foundation of Medusa’s default workflows and APIs.
How they work in practice: When a user calls the add-to-cart endpoint, the add-to-cart workflow runs which uses:
- The Product Module to check if the product exists
- The Inventory Module to ensure the product is available in inventory
- The Cart Module to add the product to the cart
Each Commerce Module’s service registers in the Medusa container, so you can resolve it in custom workflows.
Key Commerce Modules
Product Module
- Manages products with customizable attributes, options (color, size), and variants
- Supports product organization via categories, collections, and tags
- Inventory kits for bundled/multi-part product scenarios
- Tiered pricing and price rules applied to product variants
- Accessed through workflows using steps from
@medusajs/medusa/core-flows
Inventory Module
- Multi-location inventory management (warehouses)
- Reservation system: reserve quantities at specific locations for orders
- Availability verification before purchase
- Inventory kits for product bundles
- Integrates with Product Module via workflows
Payment Module
- Authorize, capture, and refund payments
- Payment collections for aggregating payments (carts, etc.)
- Third-party provider integration (Stripe, custom providers)
- Customer payment method storage
- Webhook event handling from external payment services
- Configurable options for customization
Auth Module
- Email/password authentication
- Third-party auth (Google, GitHub, etc.)
- Custom actor types (managers, custom roles)
- Custom auth providers
- Uses
emailpassmethod type with authorization scope - Services accept auth method type, request details, and scope; return success status and identity
2. Module Architecture & Fundamentals
A module is “a reusable package of functionalities related to a single domain or integration.” Modules centralize custom business logic without requiring separate applications.
Module Structure
src/modules/[module-name]/
├── models/ # Data models (database tables)
│ └── [name].ts
├── loaders/ # Optional: initialization functions
│ └── [name].ts
├── service.ts # Service class with CRUD operations
└── index.ts # Module definition export
Three Essential Components
-
Data Models - Define database tables using Medusa’s Data Model Language (DML)
- Use
model.define()with table name and schema - Automatically include
created_at,updated_at,deleted_at - Property types:
model.id(),model.text(), etc.
- Use
-
Service Class - Extends
MedusaServicefor auto-generated CRUD- Pass data models to
MedusaService({ModelName}) - Auto-generates methods like
createBrands,retrieveBrand,listBrands, etc.
- Pass data models to
-
Module Definition -
index.tsexports usingModule()utility- Registers service in Medusa’s container under the module name
Module Isolation (v2 Key Concept)
In v2, data models and services MUST live inside a module. You cannot create them separately. Each module is isolated and reusable.
3. Creating a Custom Module (Step-by-Step)
Example: Brand Module
Step 1: Create Data Model
// src/modules/brand/models/brand.ts
import { model } from "@medusajs/framework/utils"
export const Brand = model.define("brand", {
id: model.id().primaryKey(),
name: model.text(),
})Step 2: Create Service
// src/modules/brand/service.ts
import { MedusaService } from "@medusajs/framework/utils"
import { Brand } from "./models/brand"
class BrandModuleService extends MedusaService({
Brand,
}) {}
export default BrandModuleServiceStep 3: Export Module Definition
// src/modules/brand/index.ts
import { Module } from "@medusajs/framework/utils"
import BrandModuleService from "./service"
export const BRAND_MODULE = "brand"
export default Module(BRAND_MODULE, {
service: BrandModuleService,
})Step 4: Register in Configuration
// medusa-config.ts
module.exports = defineConfig({
// ...
modules: [
{
resolve: "./src/modules/brand",
},
],
})Step 5: Generate and Run Migrations
npx medusa db:generate brand
npx medusa db:migrate4. Module Loaders
Loaders are functions exported by modules that execute when the Medusa application starts. They run before the module’s main service instantiates.
Use Cases
- Establish external database connections (e.g., MongoDB)
- Register custom resources in the module’s container
- Validate prerequisites (throw errors to prevent module initialization)
Creating a Loader
- Create a file in
src/modules/[name]/loaders/[loader-name].ts - Export an async function receiving
LoaderOptions(contains module container and config options) - Add loader to module’s
index.tsin theloadersarray
Key Details
- Execution timing: Before service instantiation on app startup; also during migrations
- Keep lightweight: Loaders block startup. Use scheduled jobs or subscribers for heavy operations
- Container registration: Use
asValue()from Awilix to register resources - Module isolation: Loaders work within the isolated module container
Example Pattern (MongoDB)
- Accept connection params via module options
- Validate required configuration
- Create and register a MongoDB client using
asValue() - Inject the registered client into module services
5. Middleware
Middleware functions execute when a request is sent to an API route, before the route handler. Medusa builds on Express, so any Express middleware works.
Two Categories
- Global Middleware: Applied across all routes matching a pattern (e.g.,
/custom*) - Route Middleware: Applied to specific routes + HTTP methods
Implementation
// src/api/middlewares.ts
import { defineMiddlewares } from "@medusajs/medusa"
export default defineMiddlewares({
routes: [
{
matcher: "/custom*", // pattern
middlewares: [myMiddleware], // array of functions
method: "POST", // optional: specific HTTP verb
},
],
})Middleware Function Parameters
MedusaRequestobjectMedusaResponseobjectMedusaNextFunction- MUST be called to continue the stack
Execution Order
Core global middlewares → Plugin middlewares → Custom global middlewares → Route-specific handlers
Common Use Cases
- Protecting routes (authentication)
- Request validation (query/body params)
- Parsing non-JSON content types
- CORS configuration
6. Storefront Development
Medusa separates the storefront as an independent application from the server and admin dashboard.
Two Options
- Next.js Starter - Ready-made storefront with commerce features
- Custom Build - Any frontend framework
API Integration
- Storefronts communicate via the Store API
- Requires a publishable API key in request headers for
/storeroutes - The key scopes requests to sales channels and ensures correct inventory/pricing
Next.js Starter Setup
Combined install:
npx create-medusa-app@latest --with-nextjs-starter- Backend:
{project-name}/athttp://localhost:9000 - Admin:
http://localhost:9000/app - Storefront:
{project-name}-storefront/athttp://localhost:8000
Separate install (for existing Medusa backend):
- Clone the GitHub repo
- Install dependencies
- Configure environment variables
Key Environment Variables:
NEXT_PUBLIC_MEDUSA_PUBLISHABLE_KEY- API authenticationMEDUSA_BACKEND_URL- Backend location (default:http://localhost:9000)NEXT_PUBLIC_STRIPE_KEY- Stripe integration (built-in support)
Requirements: Node.js v20+ LTS (below v25), Git, PostgreSQL
7. Admin Panel Customization
Two Customization Methods
- Widgets - Inject new sections into existing admin pages
- UI Routes - Create entirely new pages in the dashboard
Design Consistency
Medusa provides a UI package with ready-made components matching the dashboard’s design.
Limitations
- Cannot alter the admin dashboard’s layout, design, or existing page content (aside from widget injection)
- Cannot customize the Medusa logo
- For extensive redesign: build a separate admin using Medusa’s Admin API routes
8. Authentication
Supported Auth Methods
- Email/password (built-in
emailpassprovider) - Third-party providers (Google, GitHub)
- Custom actor types (managers, custom roles)
- Custom auth providers
Implementation
Auth flows use the Workflow system. The Auth Module Service accepts:
- Authentication method type (e.g.,
"emailpass") - Request details
- Authorization scope
Returns success status and identity information. Failed auth triggers error handling.
Middleware Integration
Auth middleware protects API routes to ensure only authenticated users can access them. Applied via the middleware configuration system.
9. File Module
Handles asset management (uploads/retrievals) for things like product images.
Storage Providers
- Local Storage (default): Files stored in
uploads/directory. Development only. - S3 and compatible services: Recommended for production (AWS S3, MinIO, etc.)
Key Constraints
- Only ONE active File Module Provider at a time
- Attempting multiple providers triggers an error
- Can create custom providers for specific integrations
Usage Pattern
- Resolve File Module service from Medusa container
- Use methods like
retrieveFile()within workflow steps - Integrates through the standard workflow system
10. Deployment
Infrastructure Requirements
- PostgreSQL - Application data
- Redis - Session management, caching, event bus, workflow engine, locking
- Minimum 2GB RAM on hosting provider
- Two application instances: Server mode + Worker mode
Worker Mode Architecture
Server instance:
MEDUSA_WORKER_MODE=serverDISABLE_MEDUSA_ADMIN=false- Handles API requests, serves admin dashboard
- Start:
cd .medusa/server && npm install && npm run predeploy && npm run start
Worker instance:
MEDUSA_WORKER_MODE=workerDISABLE_MEDUSA_ADMIN=true- Handles background tasks, scheduled jobs, subscribers
- Start:
cd .medusa/server && npm install && npm run start
Production Setup Checklist
-
Configure
medusa-config.ts:- Set
workerModeviaMEDUSA_WORKER_MODEenv var - Configure
redisUrlfor Redis connection - Control admin with
DISABLE_MEDUSA_ADMIN
- Set
-
Add predeploy script to
package.json:medusa db:migrate -
Replace development modules with production alternatives:
- Redis-based caching
- Redis event bus
- Redis workflow engine
- Redis locking provider
- Production file storage (S3)
- Production notification services
-
Set environment variables:
MEDUSA_BACKEND_URL- public URL- Database and Redis connection URLs
- CORS settings for storefront, admin, and auth origins
Railway Deployment
Railway is supported as a self-hosting option. The general deployment guide applies — provision PostgreSQL and Redis on Railway, deploy two services (server + worker), and configure environment variables accordingly. No v2-specific Railway guide exists yet; adapt the general self-host steps.
Post-Deployment
- Verify:
GET <APP_URL>/health - Admin dashboard:
<APP_URL>/app - Create admin user:
medusa userCLI command
Important Notes
- Vercel free tier cannot be used for commercial projects
- Medusa Cloud is the officially recommended deployment option (push-to-deploy, autoscaling, preview environments)
- Storefronts deploy separately; the Medusa server must be deployed first since the storefront depends on it