Dependency Injection in Node.js - 2016 edition
This is part 1 of a 3-part series on Dependency Injection in Node.
In 2015, RisingStack wrote an article about Dependency Injection (DI for short) in Node, explaining what it is and how it’s done (manually). If you haven’t read that article, I highly suggest you do that before reading this one, it will make the concepts outlined in this article more clear.
In this series, I’m going to expand on manual DI, why it sucks, and finally how we can make DI look good — maybe even better than require/imports. If you’re a fan of DI because of Angular, brace yourself; this will blow your mind.
I am going to demonstrate that DI in Node today does not have to be as tedious as it once was, thanks to a neat addition to ES6 that hasn’t received as much media coverage as it deserves: Proxies.
I’m 100% positive that as a Node developer, you’ve seen DI in some form. I’m shamelessly stealing the example from the RisingStack article:
session needs a store — that store can be anything; redis, MySQL, it does not care — it just wants a damn store!
To give a quick example, this is not DI:
We are importing the db module directly, and we therefore depend on it’s location on disk, and on that particular implementation. Most of the time that’s not a big deal, but it makes testing harder — not impossible, but harder nonetheless. Additionally, the module also assumes that the db module is ready (e.g. that a connection has been established).
If we were to convert the above to a test-friendly DI approach, it would look like this:
So what’s different? Instead of exporting an object, we export a function to make the object. The function states what dependencies it needs in order to do it’s thing.
If you’re familiar with DI in other languages like Java, C# and PHP, here’s a version using ES6 classes that might be more to your liking:
I personally prefer the functional approach — no this context to worry about.
Testing the above becomes super simple — you no longer have to worry about monkey-patching require in order to replace the database module with one that connects to a test database — just invoke the function with the module you want it to use!
If that was all the code that was needed for our app, I could end the article here and call it a day. Luckily, that’s not the case.