2.0.1 • Published 4 years ago

node-provide v2.0.1

Weekly downloads
31
License
MIT
Repository
github
Last release
4 years ago

node-provide

npm version Build Status Coverage Status

Async context based Dependency Injection for Node.JS without pain with Dependency Injection Container, dependency registration, and configuration.

  • You can use it at any place of your application without rewrite your applications architecture or other preparations or initializations.
  • Each dependency can be class, function, or any another value, and plain JavaScript object too.
  • You can override your dependencies for organizing modules architecture, or unit testing without hack standard Node.JS require mechanism.
  • You can use TypeScript or JavaScript.
  • You can create isolate context for multiple instances of your application (Dependency Injection scopes) with a different set of dependencies, overrides, and instances.

Install

npm i node-provide

Example

import { provide } from "node-provide";
// ...

class Db { /* ... */ }
class Server { /* ... */ }
// ...

// Inject dependencies using a provide function and class properties
export default class App {
  db = provide(Db);
  server = provide(Server);
  // ...
  start() {
    this.db.init();
    // ...
  }
}

// index.ts
new App().start(); // You can create an instance directly as usually class

Override dependencies

If you use modules architecture of your application you can override your dependencies.

import { override, provide } from "node-provide";

class BaseA {
  log() {
    throw new Error("log is not implemented");
  }
}

class A {
  log() {
    console.log("Log A!");
  }
}

class B {
  a = provide(BaseA);
  log() {
    this.a.log(); // Log A!
  }
}

override(BaseA, A); // After that BaseA and A dependencies will use only one instance of A
new B().log(); // "Log A!"

Unit testing

You can use assign to provide mocks into your dependencies.

// world.ts
export class World {
  hello() {
    // ...
  }
}

// hello.ts
import { provide } from "node-provide";
import { World } from "./world";

export class Hello {
  world = provide(World);

  world() {
    this.world.hello();
  }
}

// hello.test.ts
import { assign, cleanup } from "node-provide";
import { World } from "./world";
import { Hello } from "./hello";
// ...

afterEach(cleanup);

test("It works!", () => {
  const worldMock = {
    hello: jest.fn(),
  }
  assign(World, worldMock);
  new Hello().world();
  expect(worldMock.hello).toBeCalled();
})

If you use Jest for unit testing you need to add some code to your jest.config.js file.

// jest.config.js
{
  // ...
  setupFilesAfterEnv: [ "node-provide/jest-cleanup-after-each" ],
  // ...
}

This code means that after each test cached dependency instances will be clear. For another testing frameworks, you need call cleanup after each test case manually for cleanup cached instances of dependencies.

const { cleanup } = require("node-provide");
// ...
afterEach(cleanup);
// ...

Isolate Dependency Injection context

If you want more then one instance of your application with different configuration or with a different overrides of dependencies, you can use zone. It works using async context for separate Dependency Injection scopes. Node.JS async hook will be created only once after the first call of zone. In each of zone section, you can define any overrides, scopes can be nested with inherit overrides.

import { zone, provide } from "node-provide";

class A {
  private counter: number = 0;
  inc() {
    this.counter += 1;
  }
  print() {
    console.log(`Counter ${this.counter}`);
  }
}

class B {
  a = provide(A);
  incAndPrint() {
    a.inc();
    a.print();
  }
}

// Each section of `zone` use different dependency injection scopes and different instances of your dependencies
await zone(() => {
  const b = new B;
  b.incAndPrint(); // Counter 1
  b.incAndPrint(); // Counter 2
});
await zone(() => {
  const b = new B;
  b.incAndPrint(); // Counter 1
});

API Reference

resolve

Returns instance of your dependency. Each dependency can be class, function or any value.

  • For class. The class will be instantiated once and cached
  • For function. The function will be called and result cached
  • For any value. Return it value without any changes
const depInstance = resolve(Dep);

provide

The function for providing an instance of dependency on the class property.

class {
  dep1 = provide(Dep1);
  dep2 = provide(Dep2);
}

override

Override dependency.

override(FromDep, ToDep);
// ...
console.log(resolve(FromDep) === resolve(ToDep)); // true

assign

Define any value as resolved value for any dependency.

assign(Dep, value);
// ...
class A {}
assign(A, 10);
console.log(resolve(A)); // 10

zone

Run your app in isolated Dependency Injection scope. All instances cached for this instance application will be isolated from all cached instances in other scopes. All overrides defined here will be inherited for nested isolated scopes but not available for others. No return value.

await zone(async () => {
  const app = new App(); // Run you app here
  await app.run();
  // ...
});
await zone(async () => {
  override(Dep1, Dep2);

  await zone(async () => {
    override(Dep2, Dep3);
    // ...
    console.log(resolve(Dep1) instanceof Dep3); // true
  });
  // ...
  console.log(resolve(Dep1) instanceof Dep2); // true
})

cleanup

Clean all cached dependency instances. It's needed for testing. Has no parameters.

// ...
afterEach(cleanup);
// ...

reset

Clean all cached dependency instances and overrides. Has no parameters.

reset()

factory

Make new DI.

const { provide, assign, override, cleanup, reset } = factory();

If you have questions or something else for me or this project, maybe architectures questions, improvement ideas or anything else, please make the issue.

2.0.1

4 years ago

2.0.0

4 years ago

1.0.1

4 years ago

1.0.0

4 years ago

0.4.4

4 years ago

0.4.3

4 years ago

0.4.2

4 years ago

0.4.1

4 years ago

0.4.0

4 years ago

0.3.0

4 years ago

0.2.8

5 years ago

0.2.7

5 years ago

0.2.6

5 years ago

0.2.5

5 years ago

0.2.4

5 years ago

0.2.3

5 years ago

0.2.2

5 years ago

0.2.1

5 years ago

0.2.0

5 years ago

0.1.7

5 years ago

0.1.6

5 years ago

0.1.5

5 years ago

0.1.4

5 years ago

0.1.3

5 years ago

0.1.2

5 years ago

0.1.1

5 years ago

0.0.0

5 years ago