Security News
Fluent Assertions Faces Backlash After Abandoning Open Source Licensing
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.
Simple, Decorated, Pluggable dependency-injection container for Node JS apps
Simple, Decorated, Pluggable dependency-injection container for Node JS apps
Injex creates a dependency tree between your modules. Using TypeScript decorators, you can define and inject modules into other modules as dependencies. A dependency is an object which can be injected into other dependent objects.
In software engineering, dependency injection is a technique whereby one object supplies the dependencies of another object. A "dependency" is an object that can be used, for example, as a service. Instead of a client specifying which service it will use, something tells the client what service to use. The "injection" refers to the passing of a dependency (a service) into the object (a client) that would use it.
From Wikipedia
Install Injex using NPM or Yarn:
npm install --save injex
Or
yarn add injex
// index.ts
await Injex.create({
rootDirs: [
path.resolve(__dirname, "./src")
]
})
.bootstrap()
// src/mailService.ts
@define()
@singleton()
export class MailService implements IMailService {
public sendMessage(message: string) {
console.log(`Sending message: ${message}...`);
}
}
// src/mailManager.ts
@define()
@singleton()
export class MailManager {
@inject() private mailService: IMailService;
public send(message: string) {
this.mailService.sendMessage(message);
}
}
// src/bootstrap.ts
@bootstrap()
export class Bootstrap implements IBootstrap {
@inject() private mailManager: MailManager;
public run() {
console.log("Ready.");
this.mailManager.send("Hello world!");
}
}
> node index
Ready.
Sending message: Hello world!...
Check out the Quickstart guid for more details.
Check out the example project if you want to see Injex in action.
A project should use TypeScript with the experimentalDecorators
compiler flag set to true
, for more information about this flag, read the TypeScript docs about decorators.
Each defined module should be exported from its file so Injex can find and register it into the container. You can use any export method (e.g. export ...
, export default ...
, module.export =
).
Sometimes you want to add objects to the container manually, Use the addObject
container method like so:
const car = {
model: "Ford",
type: "Mustang",
color: "Black"
};
container.addObject(car, "myCar");
expect(container.get("myCar")).toStrictEqual(car);
container.bootstrap();
Now you can inject "myCar" into other modules using the @inject()
decorator.
@define()
@singleton()
export class CarService {
@inject() private myCar: ICar;
@init()
public initialize() {
console.log(myCar.type); // Mustang
}
}
To remove an object, use the removeObject
container method:
container.removeObject("myCar");
expect(container.get("myCar")).toBeUndefined();
Injex supports plugins by exporting container hooks to manipulate, intercept, and extend the container abilities. Under the hood, Injex uses the tapable module by webpack, So if you had created a webpack plugin or you know how they work, you can easily create your Injex plugins.
Injex-Plugin is just a class (or simple object) with an apply
method to be invoked with the container instance once created. For example:
class MyAwesomeNotificationsPlugin {
apply(container) {
// apply container hooks here, for example:
container.hooks.beforeRegistration.tap("MyAwesomeNotificationsPlugin", () => {
container.logger.debug("Registration phase started...");
});
}
}
This list describes all the container hooks you can bind to. To bind with a hook, use the following syntax:
container.hooks.afterModuleCreation.tap("PluginName", (module: IModule) => {
// do something here...
});
Some hooks callbacks invoked with arguments.
For more information about the tapable module, refer to the tapable docs.
beforeModuleRequire
- Before requiring all files in project root directories. The callback function invoked with the module path.afterModuleRequire
- After requiring all files in project root directories. The callback function invoked with the module path and the module.exports
object.beforeRegistration
- Before all modules registration.afterRegistration
- After all modules registration.beforeCreateModules
- Before modules created and injected with dependencies.afterModuleCreation
- After each module created and injected with dependencies. The callback function invoked with the module: IModule
just created.afterCreateModules
- After modules created and injected with dependencies.berforeCreateInstance
- Before a module creation via singleton or factory method. The callback function invoked with the instance class constructor.When creating new Injex container, you can use the following configurations:
const container = await Injex.create({
rootDirs: [
path.resolve(process.cwd(), "./src")
],
logLevel: LogLevel.Error,
logNamespace: "Injex",
globPattern: "/**/*.js",
plugins: []
});
rootDirs: string[];
[path.resolve(process.cwd(), "./src")]
(the /src
folder inside the executable root)logLevel: LogLevel;
LogLevel.Error
,
LogLevel.Warn
,
LogLevel.Info
,
LogLevel.Debug
LogLevel.Error
logNamespace: string;
Injex
globPattern: string;
rootDirs
, this glob used to find the project files./**/*.js
plugins: IInjexPlugin[];
A list of plugins, see Plugins for more info.
Default: []
For example:
const container = await Injex.create({
...
plugins: [
new InjexExpressPlugin({
// ...plugin config...
})
]
});
All the container options are optional
bootstrap()
DuplicateDefinitionError
if there are module duplications or InitializeMuduleError
if there is an error in one of the @init
methods.get<T>([name])
undefined
if the module does not exist.addObject([object, name])
DuplicateDefinitionError
if the module is already defined.removeObject([name])
@define()
@define("myModule")
)@singleton()
@inject()
or get()
.@init()
@bootstrap()
run
method at the end of the bootstrap container phase after all modules initialized. You don't need to use @define() or @singleton() decorators when you use @bootstrap() since the bootstrap decorator automatically defines the module as a singleton. For Example:
@bootstrap()
export class ProjectBootstrapModule implements IBootstrap {
@inject() private mailManager: MailManager;
public async run(): Promise<void> {
await this.someAsyncTask();
this.mailManager.sendMessage("Bootstrap complete.");
}
}
Note that the run
method can return a Promise for async bootstrapping.
@inject()
@define()
class Mail {
...
}
@define()
@singleton()
export class MailManager {
// Inject a factory method using the module type
@inject(Mail) craeteMail: (message: string) => Mail;
// Inject a factory method using the module name
@inject("mail") craeteMail: (message: string) => Mail;
}
Feel free to open an issue or create a pull request
FAQs
Simple, Decorated, Pluggable dependency-injection container for Node JS apps
We found that injex demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Fluent Assertions is facing backlash after dropping the Apache license for a commercial model, leaving users blindsided and questioning contributor rights.
Research
Security News
Socket researchers uncover the risks of a malicious Python package targeting Discord developers.
Security News
The UK is proposing a bold ban on ransomware payments by public entities to disrupt cybercrime, protect critical services, and lead global cybersecurity efforts.