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!