Getting started

Installation

Deno

deno add @arthur-fontaine/diabolo

You can change your import map (in deno.json) to allow for imports like import * as DI from 'diabolo'.

{
  "imports": {
-    "@arthur-fontaine/diabolo": "jsr:@arthur-fontaine/diabolo"
+    "diabolo": "jsr:@arthur-fontaine/diabolo"
  }
}

Other environments

echo "@jsr:registry=https://npm.jsr.io" >> .npmrc
pnpm i diabolo@npm:@jsr/arthur-fontaine__diabolo

See why we use JSR instead of NPM here: https://jsr.io/docs/why.

What is dependency injection?

Dependency injection is a technique used to create loosely coupled software components, enhancing the modularity, to make the software easier to maintain and test.

A common use case for dependency injection is to create an API service. In your application, you can implement the API service using a real network request, and in your tests, you can implement the API service using mock data.

That’s where dependency injection comes in. However, using dependency injection in the JavaScript ecosystem is not a common practice, despite its benefits.

What is Diabolo?

Diabolo is a library that provides a simple and type-safe way to implement dependency injection in JavaScript. It is inspired by the Effect library.

Example

import * as DI from 'diabolo'

interface AdderService extends DI.Service<'Adder', {
  add: (a: number, b: number) => number
}> { }

const adderService = DI.createService<AdderService>('Adder')

const adderServiceImpl = DI.lazyCreateServiceImpl<AdderService>(() => ({
  add: (a: number, b: number) => a + b,
}))

const mainFunction = DI.createFunction(function* () {
  const adder = yield * DI.requireService(adderService)
  return adder.add(1, 2)
})

const result = DI.provide(mainFunction, {
  Adder: adderServiceImpl,
})()

// result === 3

Comparison with other libraries

Effect

Effect is a library with a large (too large?) scope. At the time I am writing this, the “Unpacked size” of Effect on NPM is 16.3 MB. In comparison, the “Unpacked size” of Diabolo is only a few dozen KB.

Among other things, Effect provides a way to manage dependency injection. To achieve dependency injection in Effect, you need to use at least 3 modules: Effect, Context, and Layer. Effect has 305 exported members, Context has 14 exported members, and Layer has 79 exported members. That can make it difficult to understand how to use the library.

Also, as mentioned above, the purpose of dependency injection is decoupling. However, Effect describes itself as “the missing standard library for TypeScript”. Depending on how you use Effect, it may be contrary to the principle of decoupling.

Diabolo, on its side, is a library with a very small scope. It only provides a way to manage dependency injection. Its exported members can be counted on the fingers of one hand. It is very easy to understand how to use the library.

InversifyJS and TSyringe

InversifyJS and TSyringe are two popular dependency injection libraries for TypeScript.

As just mentioned, they are dependency injection libraries for TypeScript, and only for TypeScript. They cannot be used with vanilla JavaScript.

They also rely on TypeScript experimentalDecorators, which are not compatible with the TC39 decorators proposal, as TypeScript decorators are based on an old version of the proposal.

Moreover, you will likely need to install reflect-metadata to use decorators.

If you use Babel in your project, you will also need to install babel-plugin-transform-typescript-metadata.

On the other hand, Diabolo is very simple to use. It does not require any other dependencies. It is cross-platform because it relies on generators, which are available in all JavaScript environments since 2016.

Motivation

I am a big fan of the Effect library. However, it is very complex. Even the most experienced developers can have a hard time understanding the whole library. The concept I found most interesting in Effect is the dependency injection part. So I decided to create a simpler library (strongly inspired by Effect) that only focuses on dependency injection.

This way, my friends can use (and learn) dependency injection in our school projects.