I’ve been using dependency injection in Node.js apps for close to a decade, but I’ve never found a library that checked all of the boxes I was looking for. After years of hoping someone else would build it, I finally sat down and built the library I wished existed: @brianmcd/di.

@brianmcd/di is a focused library centered around these main features:

No decorators

Most DI libraries for Node.js require enabling a trifecta of problematic features: TypeScript’s experimentalDecorators + emitDecoratorMetadata and the reflect-metadata library. It’s unclear if experimentalDecorators and emitDecoratorMetadata will be supported in the future, and they don’t play nicely with modern bundlers like esbuild.

Async factories & lifecycle hooks

It’s very common to need to connect to things when your app starts, and async factories and async lifecycle hooks (onInit / onDestroy methods in classes) make this easy. This is well-supported in Nest.js, but is surprisingly missing from some other DI libraries.

Type safety

One of the problems with decorators is that type information doesn’t flow from them, so it’s easy to have the appearance of type safety without the actual type safety. In @brianmcd/di, you will get a type error if you have a mismatch between your deps array and your constructor. Furthermore, for factory providers, tokens carry actual type information.

Scoped containers

While the majority of business logic tends to live in singleton services, there is usually a set of services that should be created on demand and per request. DataLoaders in GraphQL are a perfect example of this. @brianmcd/di has built-in support for designating a set of providers as Scoped and then creating ScopedContainers that each get their own instances of these services (constructed on-demand and cached).

If this sounds like something you’ve been looking for, too, check it out on GitHub.