Skip to main content
Services in Medusa encapsulate business logic and data access for specific domains. They extend the MedusaService base class and use decorators for transaction management and event handling.

What is a Service?

A Medusa service:
  • Extends MedusaService to get automatic CRUD methods
  • Manages data access for one or more entities
  • Uses decorators for cross-cutting concerns (transactions, events)
  • Can be injected into workflows, API routes, and other services
  • Provides type-safe DTOs for inputs and outputs

Creating a Service

1

Define Your Entity

First, create your data model:
src/modules/brand/models/brand.ts
2

Create the Service Class

Extend MedusaService with your entity configuration:
src/modules/brand/services/brand-module-service.ts
The service automatically gets these methods:
  • createBrands(data: CreateBrandDTO[]): Promise<BrandDTO[]>
  • updateBrands(data: UpdateBrandDTO[]): Promise<BrandDTO[]>
  • listBrands(filters?, config?): Promise<BrandDTO[]>
  • listAndCountBrands(filters?, config?): Promise<[BrandDTO[], number]>
  • retrieveBrand(id, config?): Promise<BrandDTO>
  • deleteBrands(ids: string[]): Promise<void>
  • softDeleteBrands(ids: string[]): Promise<void>
  • restoreBrands(ids: string[]): Promise<void>
3

Add Custom Methods

Implement custom business logic:

Service Decorators

Decorators provide cross-cutting functionality for your service methods.

@InjectManager

Injects the entity manager for database operations. Use on public methods:

@InjectTransactionManager

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

@MedusaContext

Marks the context parameter for transaction and manager injection:

@EmitEvents

Automatically emits events after method execution:

Complete Service Example

Here’s a full service implementation with custom logic:
src/modules/brand/services/brand-module-service.ts

Working with Relationships

Define relationships in your entities:
src/modules/brand/models/brand.ts
src/modules/brand/models/product.ts
Query with relationships:

Error Handling

Use MedusaError for consistent error handling:
Common error types:
  • MedusaError.Types.NOT_FOUND - Resource not found
  • MedusaError.Types.INVALID_DATA - Invalid input data
  • MedusaError.Types.NOT_ALLOWED - Operation not permitted
  • MedusaError.Types.DUPLICATE_ERROR - Duplicate entry

Emitting Events

Use aggregatedEvents to emit domain events:
The framework automatically formats and emits these events through the event bus.

Best Practices

  • Use @InjectManager() for public methods
  • Use @InjectTransactionManager() for protected methods that modify data
  • Always include @MedusaContext() parameter for database operations
  • Serialize entities before returning them: this.baseRepository_.serialize()
  • Use the auto-generated CRUD methods instead of reimplementing them
  • Throw MedusaError for error handling
  • Emit events using aggregatedEvents() for important state changes
  • Keep business logic in services, not in API routes
  • Use protected methods (ending with _) for internal operations

Next Steps

Create Workflows

Compose services into workflows

Create Custom Modules

Package services into modules