ExpressJS and MongoDB with Dependency Injection

ExpressJS and MongoDB with Dependency Injection

In this article we will figure out how to set up a simple Express server using MongoDb as external data source.

The article is not an introduction into Node.js (ExpressJS) and MongoDb, basic knowledge of these tools is assumed. Instead, we will put accent on how to implement dependency injection of the MongoDb into Express app.

By the end of the article you will have a testable Express app that can be launched in correct order (of dependencies).

Setup

We will need Node.js and MongoDb installed on a computer. At the moment of writing the latest Node.js version is 14.1.0, and the latest MongoDb version is 4.2.6.

Let's initialize our new Node.js app:

mkdir express-mongodb-demo
cd express-mongodb-demo
npm init -y

The package.json file has been generated for us. Now we can install required npm modules:

npm install express mongodb

You have noticed that we are going to use native MongoDb driver for Node.js.

Writing code

We will start from the Express app itself. Let's create its .mjs file:

touch make-app.mjs

From the name of the file you guessed that we are actually initializing the Express app factory function. This approach allow us to implement dependency injection.

In the file we've just created let's place the code of the Express app with GET /todos endpoint:

import express from 'express';

export default function makeApp(todoCollection) {
  const app = express();
    
  app.get('/todos', async (req, res, next) => {
    try {
      const cursor = await todoCollection.find();
      const todos = await cursor.toArray();
      res.json(todos);
    } catch(err) {
      next(err);  // handling internal error
    }
  });
    
  return app;
}

Long story short, when a user reaches the GET /todos endpoint, the app fetches all todo records from database and returns them as application/json.

Now let's write an index script, where MongoDb connection is established and the Express app is lanched.

touch index.mjs

The code of the index script:

import { MongoClient } from 'mongodb';
import makeApp from './make-app.mjs';

const dbUrl = 'mongodb://localhost:27017';
const dbName = 'todo-db';

MongoClient.connect(dbUrl)
  .then(client => {
    const db = client.db(dbName);
    const todoCollection = db.collection('todos');
    const app = makeApp(todoCollection);
    app.listen(3000);
  });

The only thing left to do is to run the script:

node index.mjs

And that is all the trick! We asynchronously open the MongoDb connection, and once it is opened we instantiate the Express app via its factory function passing the real database collection as an argument.

Of course, in this trivial example we have omitted many things. We aren't handling the MongoDb connection errors, we hard-code values that are dependent on environment (e.g. database URL and server port). Not to mention graceful shutdown.

But the idea is that now we can cover our Express app (in fact its factory function) by an integration test, stubbing the collection parameter and thus able to simulate database behaviour.

And the best part is that this approach works for any kind and any number of external dependencies of an Express app!

Conclusion

We've just learned how to initialize an Express app that uses external services as dependencies so the app is testable. In next articles we will be writing actual integration tests for our Express app and implement graceful shutdown for it and its MongoDb dependency service. Thanks for reading!