Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/ivanprodaiko94/hekdi

Dependency Injection for node.js integrated with KOA.js
https://github.com/ivanprodaiko94/hekdi

dependency dependency-injection di injection injector koa koa-router koa2 koajs

Last synced: 3 months ago
JSON representation

Dependency Injection for node.js integrated with KOA.js

Awesome Lists containing this project

README

        

[![Build Status](https://travis-ci.org/IvanProdaiko94/hekdi.svg?branch=master)](https://travis-ci.org/IvanProdaiko94/hekdi)
[![license](https://img.shields.io/github/license/mashape/apistatus.svg)]()
[![npm](https://img.shields.io/npm/dm/hekdi.svg)](https://www.npmjs.com/package/hekdi)
[![npm](https://img.shields.io/npm/dt/hekdi.svg)](https://www.npmjs.com/package/hekdi)

# node.js Dependency Injection

### Scale your node.js app with ease.

```bash
npm i hekdi
```

![App Example](assets/draw.png)

## Basic usage:

```javascript
// imported.module.js
const { createModule } = require('hekdi');

class Dependency1 {
constructor() {
this.name = 'Dependency1';
}
}

class Dependency2 {
static get $inject() {
return ['LocalDependency'];
}

constructor(d1) {
this.name = 'Dependency2';
this.d1 = d1;
}
}

module.exports = createModule({
name: 'ImportedModule',
declarations: [
{ name: 'LocalDependency', strategy: 'singleton', value: Dependency1 },
{ name: 'PublicDependency', strategy: 'service', value: Dependency2 },
{ name: 'Arr', strategy: 'value', value: [1, 2, 3] }
],
exports: ['PublicDependency', 'Arr']
});
```

```javascript
// main.module.js
const { createModule } = require('hekdi');
const importedModule = require('./imported.module');

class Ctrl {
static get $inject() {
return ['PublicDependency', 'Arr'];
}

constructor(publicDep, arr) {
console.log(publicDep, arr);
}
}

module.exports = createModule({
name: 'SharedModule',
declarations: [
{ name: 'Controller', strategy: 'singleton', value: Ctrl },
{ name: 'ControllerAs', strategy: 'alias', value: 'Controller' }
],
imports: [ importedModule ]
})
```

```javascript
// app.js
const { DI } = require('hekdi');
const MainModule = require('./main.module');
const di = DI.create();

di.bootstrap(MainModule);

const ctrl = di.resolve('ControllerAs');
// Dependency2 { name: 'Dependency2', d1: Dependency1 { name: 'Dependency1' } } [ 1, 2, 3 ]
```

## Main concepts:

### Top level API:
Top level api is `DI` class that bootstraps main module and serves dependencies from it then.

```javascript
const { DI } = require('hekdi');
const di = DI.create();

di.module(moduleConfig) // creates new module from config

di.bootstrap(moduleConfig) // register module as main one and resolve dependencies from it

const dep = di.resolve('dependency') // return dependency that was registered to bootstrapped module according to its strategy
```

### Modularity:

DI provides modules as a structural unit of app.
- `declarations` array sets own dependencies of this module.
- `exports` array tells what dependencies are available for other modules
- `imports` array will inject exported members from other module to this one

```javascript
const { createModule } = require('hekdi');

createModule({
name: 'SomeModule',
declarations: [
{ name: 'LocalDependency', strategy: 'singleton', value: class X {} },
{ name: 'PublicDependency', strategy: 'service', value: class Y {} },
{ name: 'Arr', strategy: 'value', value: [1, 2, 3] }
],
exports: ['PublicDependency', 'Arr'], // if '*' set, module will export all of the dependencies including imported
imports: [ AnotherModuleInstance ]
});
// here 'LocalDependency' will be available for injection only for members of this module.
```

### Strategies:
- `service` - each time a new instance will be created with `new` keyword.
- `factory` - return the result of plain function call.
- `singleton` - only one instance will be created.
- `value` - just will be returned.
- `constant` - the same as `value` but can't be reassign.
- `alias` - used to create an alias for some dependency.

# Koa.js usage:

`hekdi` can be integrated with [koa.js](https://github.com/koajs/koa).

The main concept of framework integration is monkey patching of functions
that are responsible for requests handling.

While using koa hakdi monkey patches `use` method.

#### Basic usage:
```javascript
const Koa = require('koa');
const { koaDI } = require('hekdi');
const app = new Koa();

const moduleToBootstrap = {
name: 'MainModule',
declarations: [
{ name: 'ctrl', strategy: 'singleton', value: SomeClass },
{ name: 'echo',
strategy: 'value',
value: async (ctx) => {
ctx.body = ctx.request.body;
}
}
],
exports: '*'
};

koaDI(moduleToBootstrap, app);
// now di is already bootstrapped and ready to work.
// In koa app you can reach di as `app.context.di`
// In di you can get koa app as `App` dependency.
app.use({
controller: 'ctrl', // if dependency is object
action: 'middleware', // you tell which of its methods will be called
params: [1, 2, 3] // also you can pass additional params to call if needed
});

app.use({ action: 'echo' });
// you can reach some function without class creation by passing only action
// to `use` method

app.use(async (ctx) => { // you still can pass function to `use` method
ctx.body = ctx.request.body;
});

app.listen(3000)
```

### Usage with router
While using router the story is almost the same:
```javascript
'use strict';

const Koa = require('koa');
const Router = require('koa-router');
const bodyParser = require('koa-body-parser');
const { koaDI } = require('hekdi');

const app = new Koa();
const router = new Router();

const moduleToBootstrap = {
name: 'MainModule',
declarations: [
{ name: 'ctrl', strategy: 'singleton', value: SomeClass },
{ name: 'echo',
strategy: 'value',
value: async (ctx) => {
ctx.body = ctx.request.body;
}
}
],
exports: '*'
};

koaDI(moduleToBootstrap, app, router);

app.use(bodyParser());

router
.post(['/', '/test'], { action: 'echo'})
.get('/', {
controller: 'ctrl',
action: 'getHandler',
params: [1, 2, 3]
}).get('/test', async (ctx) => {
ctx.body = 'handled';
});

app
.use(router.routes())
.use(router.allowedMethods());

app.listen(3000);
```