Build custom Medusa v2 modules — DML data models, services extending MedusaService, loaders, module links, and container registration. Use when creating custom modules.
Scanned 9/7/2026
Install to Claude Code
npx -y skills add OrcaQubits/agentic-commerce-skills-plugins --skill medusa-modules --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Medusa Modules?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/orcaqubits-medusa-modules-agentic-commerce-skills-plugin)More formats (shields.io, HTML) on the badges page.
---
name: medusa-modules
description: Build custom Medusa v2 modules — DML data models, services extending MedusaService, loaders, module links, and container registration. Use when creating custom modules.
allowed-tools: Read, Write, Edit, Bash, Grep, Glob, WebSearch, WebFetch
---
# Medusa v2 Custom Modules
## Before writing code
**Fetch live docs**:
1. Fetch `https://docs.medusajs.com/learn/fundamentals/modules` for module overview
2. Web-search `site:docs.medusajs.com DML data model reference` for model property types
3. Web-search `site:docs.medusajs.com MedusaService methods` for base service API
4. Web-search `site:docs.medusajs.com module links` for linking modules together
5. Web-search `site:docs.medusajs.com module container registration` for dependency injection
## Module Concept
A module is a self-contained package of functionality in Medusa v2:
- Encapsulates data models, services, and business logic
- Registered in `medusa-config.ts` under the `modules` array
- Accessed from workflows, API routes, and subscribers via the dependency container
- Modules communicate through links, not direct imports
## Module Directory Structure
| File | Purpose |
|------|---------|
| `src/modules/my-module/models/my-entity.ts` | DML data model |
| `src/modules/my-module/service.ts` | Service extending MedusaService |
| `src/modules/my-module/loaders/seed.ts` | Optional loader (runs on startup) |
| `src/modules/my-module/migrations/` | Auto-generated by CLI |
| `src/modules/my-module/index.ts` | Module definition export |
## DML Data Models
### Property Types
| DML Type | Database Column | Notes |
|----------|----------------|-------|
| `model.id()` | Primary key UUID | Auto-generated |
| `model.text()` | `text` / `varchar` | String fields |
| `model.number()` | `integer` | Numeric fields |
| `model.bigNumber()` | `numeric` | Precise decimals (prices) |
| `model.boolean()` | `boolean` | True/false |
| `model.dateTime()` | `timestamptz` | Date/time values |
| `model.json()` | `jsonb` | Arbitrary JSON |
| `model.enum()` | `enum` | Constrained values |
| `model.array()` | `array` | Array of primitives |
### Relationship Types
| Relationship | DML Method | Description |
|-------------|-----------|-------------|
| Has One | `model.hasOne()` | One-to-one owned |
| Has Many | `model.hasMany()` | One-to-many owned |
| Belongs To | `model.belongsTo()` | Inverse of hasOne/hasMany |
| Many to Many | `model.manyToMany()` | Junction table auto-created |
### Model Definition Skeleton
```typescript
// src/modules/my-module/models/my-entity.ts
// Fetch live docs for current DML model API
import { model } from "@medusajs/framework/utils"
const MyEntity = model.define("my_entity", {
id: model.id(), name: model.text(),
// Fetch live docs for property options and relationships
})
```
## Service Pattern
Every module exposes a service that extends `MedusaService`:
```typescript
// src/modules/my-module/service.ts
// Fetch live docs for MedusaService generic signature
import { MedusaService } from "@medusajs/framework/utils"
import MyEntity from "./models/my-entity"
class MyModuleService extends MedusaService({ MyEntity }) {}
export default MyModuleService // Add custom methods as needed
```
### Inherited MedusaService Methods
| Method Pattern | Purpose |
|---------------|---------|
| `create<Entity>(data)` | Create one or many records |
| `update<Entity>(data)` | Update records by selector or ID |
| `delete<Entity>(ids)` | Soft-delete records |
| `retrieve<Entity>(id, config)` | Retrieve single record by ID |
| `list<Entity>(filters, config)` | List with filters, pagination, relations |
| `softRestore<Entity>(ids)` | Restore soft-deleted records |
The method names are auto-generated from the model name (e.g., `createMyEntity`, `listMyEntities`).
## Module Definition Export
```typescript
// src/modules/my-module/index.ts
// Fetch live docs for Module definition shape
import { Module } from "@medusajs/framework/utils"
import MyModuleService from "./service"
export const MY_MODULE = Module("my-module", {
service: MyModuleService,
})
```
## Registration in medusa-config.ts
```typescript
// Inside modules array
// Fetch live docs for module registration options
{ resolve: "./src/modules/my-module" }
```
## Module Links
Links connect data models across different modules without coupling:
| Link Type | Purpose |
|-----------|---------|
| One-to-one link | Connect a custom entity to a core entity (e.g., product) |
| One-to-many link | One core entity linked to many custom entities |
| List link | Bidirectional many-to-many between modules |
### Link Definition Skeleton
```typescript
// src/links/product-my-entity.ts
// Fetch live docs for defineLink API
import { defineLink } from "@medusajs/framework/utils"
import ProductModule from "@medusajs/medusa/product"
import { MY_MODULE } from "../modules/my-module"
```
Links are queried using the `query` utility from the Medusa container, not through direct service calls.
## Loaders
Loaders run when the module initializes (server startup):
- Seed default data
- Register event listeners
- Validate configuration
- Set up external connections
## Container Resolution
Modules register their service in the dependency injection container:
- Resolved by module key: `container.resolve("my-module")`
- Available in workflows, API routes, subscribers, and other services
- Never import a module's service directly -- always resolve from the container
## Best Practices
- One module per bounded context -- keep modules focused and cohesive
- Use DML models exclusively -- never write raw SQL or use Mikro-ORM directly
- Extend `MedusaService` for CRUD -- only add custom methods for non-standard logic
- Use module links to connect to core entities -- never modify core module tables
- Run `npx medusa db:generate` after every model change to create migrations
- Export a constant for your module key (e.g., `MY_MODULE`) and reuse it everywhere
## Common Patterns
| Pattern | When to Use |
|---------|------------|
| Custom module + link to Product | Adding metadata/features to products |
| Custom module + link to Order | Order-related extensions (loyalty, tracking) |
| Standalone module | Independent domain (CMS, analytics, reviews) |
| Module with loader | Pre-seeding data or external service setup |
Fetch the Medusa module documentation for exact DML property options, MedusaService generics, and link definition patterns before implementing.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!