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 emailpass method 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

  1. 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.
  2. Service Class - Extends MedusaService for auto-generated CRUD

    • Pass data models to MedusaService({ModelName})
    • Auto-generates methods like createBrands, retrieveBrand, listBrands, etc.
  3. Module Definition - index.ts exports using Module() 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 BrandModuleService

Step 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:migrate

4. 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

  1. Create a file in src/modules/[name]/loaders/[loader-name].ts
  2. Export an async function receiving LoaderOptions (contains module container and config options)
  3. Add loader to module’s index.ts in the loaders array

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

  1. MedusaRequest object
  2. MedusaResponse object
  3. MedusaNextFunction - 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

  1. Next.js Starter - Ready-made storefront with commerce features
  2. Custom Build - Any frontend framework

API Integration

  • Storefronts communicate via the Store API
  • Requires a publishable API key in request headers for /store routes
  • 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}/ at http://localhost:9000
  • Admin: http://localhost:9000/app
  • Storefront: {project-name}-storefront/ at http://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 authentication
  • MEDUSA_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

  1. Widgets - Inject new sections into existing admin pages
  2. 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 emailpass provider)
  • 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=server
  • DISABLE_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=worker
  • DISABLE_MEDUSA_ADMIN=true
  • Handles background tasks, scheduled jobs, subscribers
  • Start: cd .medusa/server && npm install && npm run start

Production Setup Checklist

  1. Configure medusa-config.ts:

    • Set workerMode via MEDUSA_WORKER_MODE env var
    • Configure redisUrl for Redis connection
    • Control admin with DISABLE_MEDUSA_ADMIN
  2. Add predeploy script to package.json: medusa db:migrate

  3. 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
  4. 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 user CLI 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

Sources