UNPKG

hekdi

Version:

Depedency injection framework for node integrated with koa.js

232 lines (183 loc) 5.96 kB
[![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); ```