# loopus-injection

> Dependency Injection for JS/TS

Latest version **3.0.1** (published 2022-03-29) · 0 weekly downloads

## Install

```sh
npm install loopus-injection
pnpm add loopus-injection
yarn add loopus-injection
bun add loopus-injection
```

## Health

**Score 25/100 (F)** — status: abandoned.

Positive: has types; no vulnerabilities; high quality score.

Warnings: low downloads; no esm support.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 3.0.1 |
| Published | 2022-03-29 |
| First published | 2022-03-26 |
| Weekly downloads | 0 |
| TypeScript types | bundled |
| Module format | CommonJS |
| Dependencies | 0 |
| Unpacked size | 28.8 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | loopus |
| Maintainers | loopus |
| Keywords | Dependency Injection, Composition, Configuration, JavaScript, TypeScript |

## Links

- npm: https://www.npmjs.com/package/loopus-injection
- npm.io page: https://npm.io/package/loopus-injection

## Alternatives

- [jsforce](https://npm.io/package/jsforce.md) — 851.2K weekly downloads
- [react-native-qrcode-svg](https://npm.io/package/react-native-qrcode-svg.md) — 693.5K weekly downloads
- [@salesforce/plugin-data](https://npm.io/package/@salesforce/plugin-data.md) — 394.9K weekly downloads
- [@backstage/plugin-search-common](https://npm.io/package/@backstage/plugin-search-common.md) — 308.5K weekly downloads
- [@chain-registry/types](https://npm.io/package/@chain-registry/types.md) — 38.4K weekly downloads

## Recent versions

- 3.0.1 (latest) — 2022-03-29
- 3.0.0 — 2022-03-29
- 2.0.0 — 2022-03-26

## README

# Dependency Injection library for TypeScript/JavaScript

## Introduction

Dependency Injection is a code organization technique which, when used correctly, increases maintainability.

> The intent behind dependency injection is to achieve separation of concerns of construction and use of objects. This can increase readability and code reuse.

Read more from [Wikipedia](https://en.wikipedia.org/wiki/Dependency_injection).

## Get started

### Install the package

> `npm i loopus-injection`

### Register – Configure your services

```typescript
import { service } from "loopus-injection";

class MyDependencyA {
  ...
}

class MyDependencyB {
  ...
}

class MyService {
  constructor(
    readonly depA: MyDependencyA,
    readonly depB: MyDependencyB) {}
  ...
}

// Configure injection
service(s => {
  s.scoped(); // 1 instance per scope
  return MyDependencyA;
});
service(s => {
  s.singleton(); // 1 shared instance
  return MyDependencyB;
});
service(s => {
  s.transient(); // many instances
  s.dependency(MyDependencyA);
  s.dependency(MyDependencyB);
  return MyService;
});
```

### Begin a scope

- Create a root scope with default registrations (ie registrations from your class decorators) – root scope will keep track of all your services
  ```typescript
  const rootScope = new Scope();
  ```
- Create a child scope (ex: let's say you develop an API – you would begin a child scope for each HTTP request)
  ```typescript
  const childScope = rootScope.beginScope();
  ```

### Resolve – Get instances of your services

```typescript
const myScopedService = childScope.resolve(MyScopedService);

const mySingletonService = childScope.resolve(MySingletonService);

const myTransientService = childScope.resolve(MyTransientService);
```

### Release – Cleanup your scopes

- Let's say you develop an API – you would begin a child scope for each HTTP request. In that case, you would release the child scope when the HTTP request has been handled in order to cleanup all scoped services:
  ```typescript
  childScope.release();
  ```

## Advanced concepts

### Custom registrations

For scenarios where you want to control how you service is constructed, use `Registrations` class.

```typescript
Registrations.defaults.set({
  constructor: ..., // The constructor function to use as identifier
  factory: (scope: Scope) => ..., // A callback that will be used to create instances of the service
  lifetime: Lifetime.Transient // Or Scoped, Singleton
});
```

### Composition root

In dependency injection world, _composition root_ is a central place in your app where you put all your injection configuration code.

When using this library, you could decide to put all the configuration code in a central place, or configure each service right next to the service itself. That's up to you.

### Factory Service

Let's say you want to send emails on your production environment, but when developing on your local machine you just want to log a message to the console.

The trick is to inject `Scope` class in order to resolve the proper service at runtime based on some configuration.

```typescript
// Create an abstract class to declare a contract for our email service
abstract class EmailService {
  abstract sendEmail(to: string, body: string);
}

// We want this one to be injected in DEV
class DevEmailService extends EmailService {
  sendEmail(to: string, body: string) {
    console.log("Not sending email:", to, body);
  }
}

// We want this one to be injected in PROD
class ProdEmailService extends EmailService {
  sendEmail(to: string, body: string) {
    // TODO: Send the email for real
  }
}

// This factory service will be responsible for selecting the actual email service implementation based on some environment variable (NODE_ENV)
class EmailServiceSelector() {

  // Scope is a special service which allows you to resolve other services
  constructor(private scope: Scope) {

  }

  // Note the return type: our abstract class
  select(): EmailService {
    if (process.env.NODE_ENV === "production") {
      return this.scope.resolve(ProdEmailService);
    } else {
      return this.scope.resolve(DevEmailService);
    }
  }
}

// Configure injection
service(s => {
  s.singleton();
  return DevEmailService;
});

service(s => {
  s.scoped(); // Notice how lifetimes can be different for each implementation
  return ProdEmailService;
});

service(s => {
  s.dependency(Scope);
  s.transient(); // Important: the service factory should not have a 'higher' scope than the actual services. Here it could be Transient or Scoped, but never Singleton.
  return EmailServiceSelector;
});

// Use the service factory
const scope = new Scope();
const selector = scope.resolve(EmailServiceSelector);
const emailService = selector.select();
emailService.sendEmail("js@loopus.dev", "hello!");
```

## Use with Node

Should work out of the box.

## Use with React

Should work out of the box.

---
_Source: https://npm.io/package/loopus-injection · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
