Typescript agnostic dependency injection framework
Inspired by the PHP framework Symfony and its dependency injection by file, this library wants to provide a framework agnostic way to implement inversion of control in your project.
By using this library, your code will never be changed to add decorators or something else to implement a dependency injection. All dependencies will be written in an external configuration file, making things easier to change.
⚠️ Be careful, this library is not type-safe yet but it will be, it is planned 😉
Typescript target >= es5
, however for a better usage, it is recommended to set the target to >= es6
$ npm install netero
- Create your Typescript classes:
// folder/Mailer.ts
export class Mailer {
private transport: string;
constructor(transport: string) {
this.transport = transport;
}
getTransport() {
return this.transport;
}
}
// NewsletterManager.ts
import { Mailer } from './folder/Mailer';
export class NewsletterManager {
private mailer: Mailer;
constructor(mailer: Mailer) {
this.mailer = mailer;
}
getMailerTransport() {
return this.mailer.getTransport();
}
}
- Configure your dependencies:
# config.yaml
parameters:
mailer.transport: sendmail
services:
mailer:
path: "folder/Mailer"
arguments:
- "%mailer.transport%"
newsletterManager:
path: "NewsletterManager"
arguments:
- "@mailer"
- Build your container:
// index.ts
import { Container, YamlLoader } from 'netero';
const container = new Container();
container.load(new YamlLoader('./config.yaml'));
container.compile();
const newsletterManager = container.get('@newsletterManager');
const newsletterManager = container.get('@newsletterManager');
console.log(newsletterManager); // NewsletterManager { mailer: Mailer }
console.log(newsletterManager.getMailerTransport()); // sendmail
- parameters: (optional) list of parameters that could be injected into services
- services: list of your classes or functions you want to declare for your injections
- path: relative path to your class file
- arguments: (optional) list of arguments for your class constructor
- class: (optional) in case of multiple classes in the file, specify the name of the class you want to inject
- tags: (optional) list of tags to add to the class for grouped injections
You can inject a class as an argument to an another class:
# config.yaml
services:
paymentApi:
path: 'path/to/paymentApi'
arguments: ['@stripeClient']
stripeClient:
path: 'path/to/stripeClient'
You also have an option if you have multiple classes defined in the same file:
// path/to/paymentClients.ts
export class PaypalClient {}
export class StripeClient {}
# config.yaml
services:
paypalCLient:
path: 'path/to/paymentClients'
class: 'PaypalClient'
stripeClient:
path: 'path/to/paymentClients'
class: 'StripeClient'
You can inject global parameters defined in your configuration:
# config.yaml
parameters:
defaultLanguage: 'en-US'
services:
translator:
path: 'path/to/translator'
arguments: ['%defaultLanguage%']
If you want to dynamically inject multiple classes as argument without changing the configuration of the parent class, you can use the tags
option.
All classes with the tag will be automatically injected in the parent class.
// path/to/httpClients.ts
export class HttpClients {
constructor(clients: HttpClient[]) {}
}
// path/to/axiosClient.ts
export class AxiosClient implements HttpClient {}
// path/to/gotClient.ts
export class GotClient implements HttpClient {}
# config.yaml
services:
httpClients:
path: 'path/to/httpClients'
arguments: ['#http-client']
axiosClient:
path: 'path/to/axiosClient'
tags: ['http-client']
gotClient:
path: 'path/to/gotClient'
tags: ['http-client']
If you to have different values injected by environment, like for production, staging or development, you may need to use them in your parameters.
# config.yaml
parameters:
databaseUrl: '%env(DB_URL)%'
services:
databaseClient:
path: 'path/to/databaseClient'
arguments: ['%databaseUrl%']
You can use different kind of loader files, such as :
- Yaml
# config.yaml
parameters:
mailer.transport: sendmail
services:
mailer:
path: "folder/Mailer"
arguments:
- "%mailer.transport%"
newsletterManager:
path: "NewsletterManager"
arguments:
- "@mailer"
- Json
// config.json
{
"parameters": {
"mailer.transport": "sendmail"
},
"services": {
"mailer": {
"path": "folder/Mailer",
"arguments": [
"%mailer.transport%"
],
},
"newsletterManager": {
"path": "NewsletterManager",
"arguments": [
"@mailer"
],
}
}
}
- Typescript
// config.ts
export default {
parameters: {
'mailer.transport': 'sendmail',
},
services: {
mailer: {
path: 'folder/Mailer',
arguments: [
'%mailer.transport%',
],
},
newsletterManager: {
path: 'NewsletterManager',
arguments: [
'@mailer',
],
}
},
};
Load configuration files from different sources, whatever the order of service declarations:
// index.ts
import { Container, YamlLoader } from 'netero';
const container = new Container();
container.load(new YamlLoader('./config-file-1.yaml'));
container.load(new YamlLoader('./config-file-2.yaml'));
container.compile();
Licensed under MIT.