MedusaService base class and use decorators for transaction management and event handling.
What is a Service?
A Medusa service:- Extends
MedusaServiceto 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
Create the Service Class
Extend The service automatically gets these methods:
MedusaService with your entity configuration:src/modules/brand/services/brand-module-service.ts
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>
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
Error Handling
UseMedusaError for consistent error handling:
MedusaError.Types.NOT_FOUND- Resource not foundMedusaError.Types.INVALID_DATA- Invalid input dataMedusaError.Types.NOT_ALLOWED- Operation not permittedMedusaError.Types.DUPLICATE_ERROR- Duplicate entry
Emitting Events
UseaggregatedEvents to emit domain events:
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
MedusaErrorfor 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