awilix-manager
Wrapper over awilix to support more complex use-cases
Getting started
First install the package:
npm i awilix-manager
Next, set up your DI configuration:
import { AwilixManager } from 'awilix-manager'
import { asClass, createContainer } from 'awilix'
class AsyncClass {
async init() {
}
async dispose() {
}
}
const diContainer = createContainer({
injectionMode: 'PROXY',
})
diContainer.register(
'dependency1',
asClass(AsyncClass, {
lifetime: 'SINGLETON',
asyncInitPriority: 10,
asyncDisposePriority: 10,
asyncInit: 'init',
asyncDispose: 'dispose',
eagerInject: true,
}),
)
const awilixManager = new AwilixManager({
diContainer,
asyncInit: true,
asyncDispose: true,
strictBooleanEnforced: true,
})
await awilixManager.executeInit()
await awilixManager.executeDispose()
Disabling eager injection conditionally
In some cases you may want to prevent eager injection and async disposal of some of your dependencies - e. g. when you want to disable all of your background jobs or message consumers in some of your integration tests.
You can use enabled
resolver parameter for that:
import { AwilixManager } from 'awilix-manager'
import { asClass, createContainer } from 'awilix'
class QueueConsumerClass {
async consume() {
}
async destroy() {
}
}
const diContainer = createContainer({
injectionMode: 'PROXY',
})
const isAMQPEnabled = false
diContainer.register(
'dependency1',
asClass(QueueConsumerClass, {
lifetime: 'SINGLETON',
asyncInitPriority: 10,
asyncDisposePriority: 10,
asyncInit: 'consume',
asyncDispose: 'destroy',
enabled: isAMQPEnabled,
}),
)
const awilixManager = new AwilixManager({
diContainer,
asyncInit: true,
asyncDispose: true,
strictBooleanEnforced: true,
})
await awilixManager.executeInit()
await awilixManager.executeDispose()
Note that passing undefined
or null
as a value for the enabled
parameter counts as a default, which is true
. That may lead to hard-to-debug errors, as it may be erroneously assumed that passing falsy value should equal to passing false
. In order to prevent this, it is recommended to set strictBooleanEnforced
flag to true
, which would throw an error if a non-boolean value is explicitly set to the enabled
field. In future semver major release this will become a default behaviour.
Fetching dependencies based on tags
In some cases you may want to get dependencies based on a supplied list of tags.
You can use tags
parameter in conjunction with the getWithTags
method for that:
import { AwilixManager } from 'awilix-manager'
import { asClass, createContainer } from 'awilix'
const diContainer = createContainer({
injectionMode: 'PROXY',
})
class QueueConsumerHighPriorityClass {
}
class QueueConsumerLowPriorityClass {
}
diContainer.register(
'dependency1',
asClass(QueueConsumerHighPriorityClass, {
lifetime: 'SINGLETON',
asyncInit: true,
tags: ['queue', 'high-priority'],
}),
)
diContainer.register(
'dependency2',
asClass(QueueConsumerLowPriorityClass, {
lifetime: 'SINGLETON',
asyncInit: true,
tags: ['queue', 'low-priority'],
}),
)
const awilixManager = new AwilixManager({
diContainer,
asyncInit: true,
asyncDispose: true,
})
const result1 = awilixManager.getWithTags(diContainer, ['queue'])
const result2 = awilixManager.getWithTags(diContainer, ['queue', 'low-priority'])
Mocking dependencies
Sometimes you may want to intentionally inject objects that do not fully conform to the type definition of an original class. For that you can use asMockClass
resolver:
type DiContainerType = {
realClass: RealClass
realClass2: RealClass
}
const diConfiguration: NameAndRegistrationPair<DiContainerType> = {
realClass: asClass(RealClass),
realClass2: asMockClass(FakeClass),
}
const diContainer = createContainer<DiContainerType>({
injectionMode: 'PROXY',
})
for (const [dependencyKey, dependencyValue] of Object.entries(diConfiguration)) {
diContainer.register(dependencyKey, dependencyValue as Resolver<unknown>)
}
const { realClass, realClass2 } = diContainer.cradle
expect(realClass).toBeInstanceOf(RealClass)
expect(realClass2).toBeInstanceOf(FakeClass)