Dependency Injection in Node.js - 2016 edition, part 2

Jeff Hansen
3 min readSep 6, 2016

--

This is part 2 of a 3-part series on Dependency Injection in Node.

The world ain’t all foo bar and pseudo-code. It’s a very mean and nasty place…

Let me set the stage for our imaginary, totally original project that no one ever thought of before: The Todo App. Oh yes. Patent pending…

Before we get into API frameworks and all that stuff, let’s just write the meat of the app — the service and data access. I will be using ES7 async-await for readability (and because it’s awesome).

Let’s start with the Todos Service — it handles all the “business logic” like isolating access to a specific user’s todos.

I will be writing these snippets in different styles (functional and class-oriented) to prove a point — that it does not matter! Use whichever you prefer!

todosService.js

A bit lengthy, but nothing fancy going on there. We don’t depend on any external libraries (other than the built-in assert module to add sanity checks). However, our exported function has 2 dependencies:

  • todosRepository — an object that gives us access to the todos database (whatever it may be, we don’t really care).
  • currentUser — the user that is using the service. Remember, we don’t know where it’s coming from! We don’t care!

Moving on, let’s get ourselves an implementation of that sweet sweet todos repository!

todosRepository.js

This is just an in-memory implementation of the todos repository used by the service. Whenever we are ready, we can swap it out for a MySQL, Rethink, MongoDB, WhateverDB you want, as long as the API is the same. Tools like TypeScript and Flow really shine here.

Stitching the system together

Before going into writing the actual REST API, let’s just try and stitch things together in a test. This approach is called Poor Man’s DI — don’t worry, I’ll fill you in later!

You might be thinking: “But Jeff! That code now knows about both modules!” — that’s true, and in a real app (which we will get to in a minute), there needs to be a single source of truth that knows about all the modules being used — those of us donning the DI fancy pants have a name for this holy place: The Composition Root. It is the single place in the application that ties the whole thing together — it composes your modules! Want to swap out that in-memory todosRepository for a RethinkDB implementation? This is where you go to do the swippety swappety.

The composition root could look like this.

compositionRoot.js

I know, I know what you’re thinking! “I don’t know who the user is yet, I’m building a web app! This is useless!” You are absolutely right. There are 2 ways to solve this manually:

  • Pass in the currentUser to all the methods that need it — this sucks.
  • Defer instantiating things until you have all the data you need — this also sucks, because now you need to instantiate things in multiple places.

To give you an idea of how the second approach would work, and just how much it sucks, here’s how we could do it. This example is using Koa Router.

And this is just with 2 modules! Imagine if there were 10 (and that’s just for a small app). Yep, sucks.

So we can all agree that doing DI manually in a large scale app like our amazing multi-tenant todo app simply isn’t going to work for us. Let’s just give up and go back to require. Have a great day.

… or not! Read part 3 and fix this mess!

--

--

Jeff Hansen

Full-stack JavaScript engineer straight outta Denmark, making taxes suck less @Taxfyle. Created Awilix (JS library), https://SkyClip.co (service)