DEV Community

Cover image for Dockerize a Node.js app connected to MongoDb
Vladislav Guleaev
Vladislav Guleaev

Posted on

Dockerize a Node.js app connected to MongoDb

Hello dear coder, welcome to my tech articles series dedicated to Node.js and Docker. Hope you enjoy!

Problem:

You already know how to use Docker together with Node from previous article in this series. I know that we all love MERN/MEAN stacks. Our next step is to understand how Node and Mongo connects to each other running inside containers. Lets’ go!

1. Install MongoDb locally

Time to get into some document db stuff. First of all download MongoDb server from here.

If you haven't change anything during install, it should also install a thing called MongoDb Compass Community.

This is a great tool to inspect, change, add or remove data in collections in MongoDb. You can connect to the local instance by using default address and port like on this image below or connect to any other server.

To connect locally just press connect. Inside you can see some default collections and you can play around. We will need MongoDb Compass a bit later.

2. Connect to MongoDb through Express app

In this tutorial I will be using my favorite editor Visual Studio Code. You will also need Nodejs and Docker installed. In my case I’m using Windows, so I got Docker for Windows from here.

Now run following command:



mkdir test-mongo-app && cd test-mongo-app && npm init -y && code .


Enter fullscreen mode Exit fullscreen mode

Time to install dependencies. We will need express and mongoose packages.



npm i express mongoose


Enter fullscreen mode Exit fullscreen mode

Create file called server.js inside root folder.

Also don't forget to change your package.json to run server.js file at start.



{
  "name": "test-mongo-app",
  "version": "1.0.0",
  "description": "",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1",
    "mongoose": "^5.6.1"
  }
}



Enter fullscreen mode Exit fullscreen mode

Good. Let's create a basic express app with two routes. One for reading Users from the database and second is for adding dummy user data to it.

First of all check if everything works with just express server.



// server.js
const express = require("express");
const app = express();

const PORT = 8080;

app.get("/", (req, res) => {
  res.send("Hello from Node.js app \n");
});

app.listen(PORT, function() {
  console.log(`Listening on ${PORT}`);
});



Enter fullscreen mode Exit fullscreen mode

You can run npm start to test it. If you see the message "Listening on 8080" everything is ok. Also open http://localhost:8080 and check if you can see the hello message.

There is a nice thing called nodemon. It auto rebuilds our project when any changes happened in source code. Let's use it! 😀



npm install --save-dev nodemon


Enter fullscreen mode Exit fullscreen mode

Add a new command in package.json. So we use it for development.



  "scripts": {
    "start": "node server.js",
    "dev": "nodemon server.js"
  },


Enter fullscreen mode Exit fullscreen mode

Now use run npm run dev while development instead of npm start.



npm run dev


Enter fullscreen mode Exit fullscreen mode

You will notice difference in the console, because now nodemon is watching for any changes in your project and if needs, rebuild it. Change something in server.js and you will notice 😉

Now create folder src in root of the project. Here we will add all the rest files.

Let's create a User model for mongoose. Create file names User.model.js



// User.model.js
const mongoose = require("mongoose");

const userSchema = new mongoose.Schema({
  username: {
    type: String
  }
});

const User = mongoose.model("User", userSchema);

module.exports = User;



Enter fullscreen mode Exit fullscreen mode

Good! Here we defined a model for our document db. User model has only one field username which is a string. Enough for now :)

Let's add a file called connection.js for connection to the database.



// connection.js
const mongoose = require("mongoose");
const User = require("./User.model");

const connection = "mongodb://localhost:27017/mongo-test";

const connectDb = () => {
  return mongoose.connect(connection);
};

module.exports = connectDb;



Enter fullscreen mode Exit fullscreen mode

Please notice that mongo-test will be the name of our database (cluster).

Now modify a bit server.js and start the app. You should see message in the console that MongoDb is connected.



// server.js
const express = require("express");
const app = express();
const connectDb = require("./src/connection");

const PORT = 8080;

app.get("/users", (req, res) => {
  res.send("Get users \n");
});

app.get("/user-create", (req, res) => {
  res.send("User created \n");
});

app.listen(PORT, function() {
  console.log(`Listening on ${PORT}`);

  connectDb().then(() => {
    console.log("MongoDb connected");
  });
});




Enter fullscreen mode Exit fullscreen mode

Yeah! 🎉 We connected Express app with local MongoDb instance!

3. Implement read and write to MongoDb

We should implement two routes for reading and adding new users.
Open server.js file and first of all import our model on the top:



// server.js
const User = require("./src/User.model");
// ...


Enter fullscreen mode Exit fullscreen mode

Then implement both routes below like this:



// server.js
app.get("/users", async (req, res) => {
  const users = await User.find();

  res.json(users);
});

app.get("/user-create", async (req, res) => {
  const user = new User({ username: "userTest" });

  await user.save().then(() => console.log("User created"));

  res.send("User created \n");
});
// ...


Enter fullscreen mode Exit fullscreen mode

Be attentive here we are using async/await pattern. If you are curious about this find it here.

Basically we implemented two routes /users and /user-create. ✋ Yeah, yeah, I know that create should be done through the POST http verb but just to make testing easier and escape configuring seed method for db.

Now it's time to test! 🔍 Open in browser this link http://localhost:8080/user-create to create a dummy user record in db. Open this link http://localhost:8080/users to get all users as JSON in browser.

After doing that you can go back to MongoDb Compass and check users collection here. You should see this

4. Dockerize Node and MongoDb

Add Docker file to the root folder.



touch Dockerfile


Enter fullscreen mode Exit fullscreen mode

Paste following inside it:



FROM node:8
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
COPY package*.json ./

RUN npm install
# Copy app source code
COPY . .

#Expose port and start application
EXPOSE 8080
CMD [ "npm", "start" ]


Enter fullscreen mode Exit fullscreen mode

We can simply build our express app with this command



docker build -t mongo-app .


Enter fullscreen mode Exit fullscreen mode

But.. this will only run our express app, but not together with MongoDb. That's why we need a docker-compose file. 🐳

Now create another file called docker-compose.yml and paste this:



version: "2"
services:
  web:
    build: .
    ports:
      - "8080:8080"
    depends_on:
      - mongo
  mongo:
    image: mongo
    ports:
      - "27017:27017"



Enter fullscreen mode Exit fullscreen mode

We defined 2 services in this file. One is our node app running on port 8080 and the other is mongodb instance.

⚠️ Before you run next command, please make sure that you changed connection string to mongo db in connection.js file.



const connection = "mongodb://mongo:27017/mongo-test";


Enter fullscreen mode Exit fullscreen mode

We replaced localhost with mongo which is very important. Because we should tell the app that we want to access MongoDb from docker internal virtual network and not the local one.

Now run the magic command 🔮



docker-compose up


Enter fullscreen mode Exit fullscreen mode

Open a browser on http://localhost:8080/users and http://localhost:8080/user-create to see our app running in Docker.

(In case anything doesn't work, try to stop/remove image and containers, rebuild it by ruining docker compose-up again and if mongo image is not pulled from hub try to re-login in into docker hub or restart docker for Windows)

See the source code here. Enjoy!


🚀 If you read something interesting from that article, please like and follow me for more posts. Thank you dear coder! 😏

Top comments (11)

Collapse
 
rknell profile image
Ryan Knell

Awesome, but what is the right way to take this into production? Do I clone a repo and run docker compose up on the server, or is there some relatively easy way to co-ordinate all this? I'm sure it's easy but I've never really got my head around it.

Collapse
 
vguleaev profile image
Vladislav Guleaev

There are many ways to do it, I will try to explain it in future articles, thanks for the feedback

Collapse
 
pclewisnz profile image
pclewisnz

In Dec 2020 I have followed this tutorial and it's the only one of the eight I have reviewed that works. It's well explained, well documented, excellent citations to allow further reading and apart from the thing I noted in Part 1, I was able to follow along in VSCode perfectly. Thanks for the tutorial! now on to Part 3!

Collapse
 
lysofdev profile image
Esteban Hernández

Great article but why not use the docker images from the start? That way you can avoid the installation steps.

Collapse
 
vguleaev profile image
Vladislav Guleaev • Edited

Sorry still learning how to better structure article tutorials, will improve it

Collapse
 
maxmaxymenko profile image
Max Maxymenko

One of the greatly detailed articles. Really enjoyed reading it. ❤️

Collapse
 
vguleaev profile image
Vladislav Guleaev

Thank you!

Collapse
 
to_code profile image
Thinking out code

Simple and good explanation.

Collapse
 
sivakumarbalu profile image
SivakumarBalu

Great Article! Enjoyed reading it.

Collapse
 
vguleaev profile image
Vladislav Guleaev

Thank you!

Collapse
 
bijay_ps profile image
BIJAY PRAKASH SINGH

Awesome I loved it. Also what if I want to add some data to mongo db, which is running from docker, via terminal before hand, say an Admin user. How can I do that?