Dependency Injection
Medusa uses Awilix as its dependency injection (DI) container to manage service dependencies, promote loose coupling, and enable testability. The container automatically resolves and injects dependencies when services are accessed.What is Dependency Injection?
Dependency Injection is a design pattern where objects receive their dependencies from an external source rather than creating them internally. This enables:- Loose coupling - Services don’t depend on concrete implementations
- Testability - Dependencies can be mocked or stubbed
- Modularity - Services can be swapped or extended
- Lifecycle management - Container controls object creation and disposal
Medusa’s DI container is based on Awilix, a powerful IoC container for Node.js that supports constructor injection, lifetime management, and automatic resolution.
The Container
The Medusa container is an Awilix container that holds all registered services, modules, and dependencies:packages/core/core-flows/src/api-key/steps/create-api-keys.ts:35-36
Service Registration
Automatic Registration
Medusa automatically registers:- Module services - All module services from
packages/modules/ - Core services - Framework services (logger, event bus, etc.)
- Custom services - Services in your
src/services/directory
Module Registration
Modules self-register using theModule factory:
ApiKeyModuleService under the key Modules.API_KEY (which equals "apiKey").
Source: packages/modules/api-key/src/index.ts
Manual Registration
Register services manually in loaders:Registration Modes
Awilix provides three registration modes:asClass
Registers a class that will be instantiated:asFunction
Registers a factory function:asValue
Registers a constant value:Lifetime Management
Control how long instances live:Singleton (Default)
One instance for the entire application:Scoped
New instance per scope (request):Scoped instances are created once per request scope. Use for services that maintain request-specific state.
Transient
New instance every time:Constructor Injection
Services receive dependencies through their constructor:packages/modules/api-key/src/services/api-key-module-service.ts:39-63
Resolution Patterns
In Workflow Steps
Steps receive the container in their context:In API Routes
API routes access the container viareq.scope:
In Subscribers
Event subscribers receive the container:Container Registration Keys
Medusa provides constants for common services:Scoped Containers
Create child containers for isolated scopes:Testing with DI
DI makes testing easier by allowing mock dependencies:Best Practices
Use Constructor Injection
Always inject dependencies through the constructor, not properties.
Depend on Interfaces
Type dependencies with interfaces, not concrete classes.
Prefer Singleton
Use singleton lifetime for stateless services to reduce memory.
Avoid Container in Services
Don’t inject the container itself - inject specific dependencies.
Common Patterns
Service with Multiple Dependencies
packages/modules/product/src/services/product-module-service.ts:155-190
Optional Dependencies
Mark optional dependencies with?:
Next Steps
Services
Learn how to build services with DI
Modules
Understand Medusa’s module architecture