> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/medusajs/medusa/llms.txt
> Use this file to discover all available pages before exploring further.

# Create Custom Modules

> Learn how to create custom modules in Medusa using the modules-sdk.

Custom modules allow you to extend Medusa with domain-specific business logic while maintaining the framework's architectural patterns. Modules are self-contained units that can manage their own data models, services, and business logic.

## What is a Module?

A module in Medusa is a package that:

* Encapsulates specific domain logic (e.g., product management, inventory)
* Manages its own data models and database schema
* Exposes a service interface for other parts of the application
* Can be easily replaced or extended

## Creating a Basic Module

<Steps>
  <Step title="Define the Module Entry Point">
    Create an `index.ts` file that exports your module using the `Module` utility:

    ```typescript title="src/modules/brand/index.ts" theme={null}
    import { Module, Modules } from "@medusajs/framework/utils"
    import { BrandModuleService } from "./services"

    export const BRAND_MODULE = "brandModuleService"

    export default Module(BRAND_MODULE, {
      service: BrandModuleService,
    })
    ```
  </Step>

  <Step title="Create Data Models">
    Define your entities using MikroORM decorators:

    ```typescript title="src/modules/brand/models/brand.ts" theme={null}
    import { Entity, Property } from "@mikro-orm/core"
    import { BaseEntity } from "@medusajs/framework/utils"

    @Entity()
    export class Brand extends BaseEntity {
      @Property()
      name: string

      @Property({ nullable: true })
      description?: string

      @Property({ nullable: true })
      logo_url?: string
    }
    ```

    Export your models:

    ```typescript title="src/modules/brand/models/index.ts" theme={null}
    export { Brand } from "./brand"
    ```
  </Step>

  <Step title="Create the Module Service">
    Extend `MedusaService` to create your module's main service:

    ```typescript title="src/modules/brand/services/brand-module-service.ts" theme={null}
    import { Context, DAL, InternalModuleDeclaration } from "@medusajs/framework/types"
    import {
      InjectManager,
      InjectTransactionManager,
      MedusaContext,
      MedusaService,
      ModulesSdkTypes,
    } from "@medusajs/framework/utils"
    import { Brand } from "../models"

    type InjectedDependencies = {
      baseRepository: DAL.RepositoryService
      brandService: ModulesSdkTypes.IMedusaInternalService<any>
    }

    export default class BrandModuleService extends MedusaService<{
      Brand: {
        dto: BrandDTO
      }
    }>({
      Brand,
    }) {
      protected baseRepository_: DAL.RepositoryService
      protected readonly brandService_: ModulesSdkTypes.IMedusaInternalService<Brand>

      constructor(
        { baseRepository, brandService }: InjectedDependencies,
        protected readonly moduleDeclaration: InternalModuleDeclaration
      ) {
        super(...arguments)
        this.baseRepository_ = baseRepository
        this.brandService_ = brandService
      }

      // Custom methods
      @InjectManager()
      async findByName(
        name: string,
        @MedusaContext() sharedContext: Context = {}
      ): Promise<Brand | null> {
        const [brand] = await this.brandService_.list(
          { name },
          { take: 1 },
          sharedContext
        )
        return brand || null
      }
    }
    ```

    The `MedusaService` base class automatically provides:

    * `createBrands()` - Create one or more brands
    * `updateBrands()` - Update brands
    * `listBrands()` - List brands with filtering
    * `listAndCountBrands()` - List with pagination
    * `retrieveBrand()` - Get a single brand by ID
    * `deleteBrands()` - Delete brands
    * `softDeleteBrands()` - Soft delete brands
    * `restoreBrands()` - Restore soft-deleted brands
  </Step>

  <Step title="Define Type Definitions">
    Create TypeScript interfaces for your DTOs:

    ```typescript title="src/modules/brand/types/index.ts" theme={null}
    export interface BrandDTO {
      id: string
      name: string
      description?: string
      logo_url?: string
      created_at: Date
      updated_at: Date
    }

    export interface CreateBrandDTO {
      name: string
      description?: string
      logo_url?: string
    }

    export interface UpdateBrandDTO {
      id: string
      name?: string
      description?: string
      logo_url?: string
    }
    ```
  </Step>

  <Step title="Register the Module">
    Add your module to `medusa-config.ts`:

    ```typescript title="medusa-config.ts" theme={null}
    import { defineConfig } from "@medusajs/framework/utils"
    import { Modules } from "@medusajs/framework/utils"

    export default defineConfig({
      // ... other config
      modules: [
        {
          resolve: "./src/modules/brand",
          options: {
            // Module-specific options
          },
        },
      ],
    })
    ```
  </Step>
</Steps>

## Using Service Decorators

Medusa provides decorators for common service patterns:

### @InjectManager

Injects the database entity manager into the method context. Use this for public methods:

```typescript theme={null}
@InjectManager()
async createBrand(
  data: CreateBrandDTO,
  @MedusaContext() sharedContext: Context = {}
): Promise<BrandDTO> {
  return await this.createBrands([data], sharedContext).then((r) => r[0])
}
```

### @InjectTransactionManager

Injects a transactional entity manager. Use this for protected methods that modify data:

```typescript theme={null}
@InjectTransactionManager()
protected async createBrand_(
  data: CreateBrandDTO,
  @MedusaContext() sharedContext: Context = {}
): Promise<Brand> {
  const brand = this.brandService_.create(data, sharedContext)
  return brand
}
```

### @MedusaContext

Marks a parameter as the shared context, which contains the transaction manager and metadata:

```typescript theme={null}
async listBrands(
  filters: FilterableBrandProps = {},
  config: FindConfig<BrandDTO> = {},
  @MedusaContext() sharedContext: Context = {}
) {
  return await this.brandService_.list(filters, config, sharedContext)
}
```

## Module Configuration

You can define configuration options for your module:

```typescript title="src/modules/brand/types/index.ts" theme={null}
export interface BrandModuleOptions {
  defaultLogoUrl?: string
  enableBrandValidation?: boolean
}
```

Access configuration in your service:

```typescript theme={null}
const options = this.moduleDeclaration.options as BrandModuleOptions
const logoUrl = data.logo_url || options.defaultLogoUrl
```

## Best Practices

* Keep modules focused on a single domain
* Use the base `MedusaService` methods instead of reimplementing CRUD operations
* Apply `@InjectManager()` to public methods and `@InjectTransactionManager()` to protected methods
* Always include `@MedusaContext()` parameter for database operations
* Define clear DTO interfaces for type safety
* Use soft deletes for data that may need to be restored

## Next Steps

<CardGroup cols={2}>
  <Card title="Create Workflows" icon="diagram-project" href="/development/workflows">
    Compose multi-step business processes
  </Card>

  <Card title="Create Services" icon="gear" href="/development/services">
    Build internal services for module logic
  </Card>
</CardGroup>
