@azera/container
TypeScript icon, indicating that this package has built-in type declarations

2.1.5 • Public • Published

Azera Container

Intro

Azera container is a dependency injection service container for JavaScript written in Typescript.

First simple example :

import {Container} from "@azera/container";

let container = new Container;

container.set('logger', class Logger {
  log(message) {}
})

let app = container.invoke(['logger', class App {
  constructor(logger) {
    logger.log('Initialize app');
  }
}]);

Simple injection with decorators in Typescript :

import {Service, Container} from "@azera/container";

class Logger {
  @Inject('loggerNS') namespace: string;
  log(message) { console.log(`[${this.namespace}] ${message}`); }
}

@Inject([ Logger ]) class App {
  constructor(private logger: Logger) {}
  init() { this.logger.log('Initailize application'); }
}

let container = new Container();
// Set a parameter
container.setParameter('loggerNS', 'app');
// Create an instance from App
let app = container.invoke(App);
app.init();
// Console output : "[app] Initialize application"

Property injection

class App {
  @Inject('logger') logger: Logger;
}

Simply !

Container-aware class

import {ContainerAware, Container} from "@azera/container";

@Service('logger') class Logger {
  log(message) { console.log(message); }
}

class App extends ContainerAware() {
  init() {
    this.container.get('logger').log('Initialize app');
  }
}

let container = new Container();
container.add(Logger);
let app = container.invoke(App);
app.init(); // output: Initialize app

Factory

You can also use factories to generate services, only add Factory to the end of function name :

import {Container} from "@azera/container";

class Logger {
  log() {}
}

container.set('logger', function loggerFactory() {
  return new Logger;
});

let logger: Logger = container.get('logger');
// Or
let logger = container.get<Logger>('logger');

Tags

Also you can define tag for services :

import {Container, Tag} from "@azera/container";

abstract class Command { }

@Tag('command') class RunCommand extends Command {}
@Tag('command') class HelpCommand extends Command {}

let container = new Container;
container.add(RunCommand, HelpCommand);

let commands: Command[] = container.getByTag<Command>('command');
// Or inject them
class ConsoleApp {
  @Inject('$$command') commands: Command[];
}

Auto-Tagging

You can do tagging automatically :

import {Container} from "@azera/container";

abstract class Command { }

class RunCommand extends Command {}
class HelpCommand extends Command {}

container
  .autoTag(Command, [ 'commands' ])
  .add(RunCommand, HelpCommand);
  
class ConsoleApp {
  // When property is array it will assumes as tagged an its named used as tag name
  @Inject() commands: Command[];

  // When when property name differs from tag name we can use $$[tagName] as service name to inject tags 
  @Inject('$$commands') commandsList: Command[];
}

let app = container.invoke(ConsoleApp);

Create custom auto tagging function

container.autoTag(definition => {
  return definition.name.endsWith('Command') ? ['command'] : []
})

Predefined Services and Parameters

// services.ts
import {Inject} from "@azera/container";

export default {


  app: class App {

    @Inject('logger')
    logger: Logger;
    
    run() {
      // Run logic
    }

  },

  // You can also declare service with Definition schema
  logger: {
    service: class Logger {
      constructor(private ns: string) {}
    },
    parameters: [ '$loggerNS' ]
  }
}
// parameters.ts
export default {
  loggerNS: 'app'
}
// index.ts
import {Container} from "@azera/container";
import Services from "./services";
import Parameters from "./parameters";

let container = new Container(Services, Parameters);
let app = container.get('app');
app.run();

Type-based injection

We can also emit service configuration and naming and use type-based injection.

// Logger.ts
export default class Logger {
  log(message: string) {
    console.log(message);
  }
}
// App.ts
import Logger form './Logger.ts'

export default class App {
  constructor(@Inject() public logger: Logger) {}
}
// index.ts
import App from './App.ts';

new Container()
  .invoke(App)
  .logger
  .log('Hello World');

Async

@Service({
  factory: async function connectionFactory() {
    let connection = new Connection();
    await connection.connect();
    return connection;
  }
})
class Connection {
  name = "default";
  async connect() { /** Connection logic **/ }
  async execute(query: string) { /** Command Exection **/ }
}

class Model {
  constructor(@Inject() connection: Connection) {}
}

async function run() {
  let container = new Container();
  let model = await container.invokeAsync(Model); // Model will resolve after conectionFactory() resolve
  let result = model.connection.execute("SELECT * FROM User");
}

Scope

class Request {
  constructor(public requestId: number) {}
}

class EntityManager {
}

const container = new Container();

container.add(EntityManager);

const scope1 = container.scope(`request-1`);
const scope2 = container.scope(`request-2`);

scope1.setAlias(Request, new Request(100));
scope2.setAlias(Request, new Request(200));

assert( scope1.invoke(Request).requestId == 100 );    // true
assert( scope2.invoke(Request).requestId == 200 );    // true
assert( scope1.invoke(EntityManger) == scope2.invoke(EntityManager) );  // true

Readme

Keywords

none

Package Sidebar

Install

npm i @azera/container

Weekly Downloads

5

Version

2.1.5

License

MIT

Unpacked Size

124 kB

Total Files

20

Last publish

Collaborators

  • mdzzohrabi