Docker Node.js and MongoDB example
Tags: docker, webdev, javascript, devops
Published: 2023-09-11 03:57:51

<a href="https://www.docker.com/">Docker</a> provides lightweight containers to run services in isolation from our infrastructure so we can deliver software quickly. In this tutorial, I will show you how to dockerize Nodejs Express and MongoDB example using <a href="https://docs.docker.com/compose/">Docker Compose</a>.

Related Posts:
<ul>
	<li><a href="https://bezkoder.com/node-express-mongodb-crud-rest-api/">Node.js Express Rest API with MongoDb</a></li>
	<li><a href="https://bezkoder.com/node-js-mongodb-pagination/">Node.js Express Pagination with Mongoose and MongoDB</a></li>
	<li><a href="https://bezkoder.com/node-js-mongodb-auth-jwt/">Node.js + MongoDB: User Authentication & Authorization</a></li>
	<li><a href="https://bezkoder.com/node-js-upload-store-images-mongodb/">Upload/store images in MongoDB using Node.js, Express & Multer</a></li>
	<li><a href="https://bezkoder.com/mongoose-one-to-one-relationship-example/">MongoDB One-to-One relationship tutorial with Mongoose example</a></li>
	<li><a href="https://bezkoder.com/mongoose-one-to-many-relationship/">MongoDB One-to-Many Relationship tutorial with Mongoose examples</a></li>
	<li><a href="https://bezkoder.com/mongodb-many-to-many-mongoose/">MongoDB Many-to-Many Relationship with Mongoose examples</a></li>
</ul>

Dockerize fullstack:
- <a href="https://www.bezkoder.com/docker-mern/">Docker Compose: React + Node.js Express + MongoDB</a>

<h2>Node.js Express and MongoDB with Docker Overview</h2>
Assume that we have a Nodejs Application working with MongoDB database.
The problem is to containerize a system that requires more than one Docker container:
<ul>
	<li>Node.js Express for API</li>
	<li>MongoDB for database</li>
</ul>

Docker Compose helps us setup the system more easily and efficiently than with only Docker. We're gonna following these steps:
<ul>
	<li>Create Nodejs App working with MongoDB database.</li>
	<li>Create Dockerfile for Nodejs App.</li>
	<li>Write Docker Compose configurations in YAML file.</li>
	<li>Set Environment variables for Docker Compose</li>
	<li>Run the system.</li>
</ul>

Directory Structure:

<img src="https://www.bezkoder.com/wp-content/uploads/2021/08/docker-compose-nodejs-mongodb-example-structure.png" alt="docker-compose-nodejs-mongodb-example-structure" width="230" height="180" class="alignnone size-full wp-image-9120" />

<h2>Create Nodejs App</h2>
You can read and get Github source code from one of following tutorials:
<ul>
	<li><a href="https://bezkoder.com/node-express-mongodb-crud-rest-api/">Node.js Express Rest API with MongoDb</a></li>
	<li><a href="https://bezkoder.com/node-js-mongodb-pagination/">Node.js Express Pagination with Mongoose and MongoDB</a></li>
	<li><a href="https://bezkoder.com/node-js-mongodb-auth-jwt/">Node.js + MongoDB: User Authentication & Authorization</a></li>
	<li><a href="https://bezkoder.com/node-js-upload-store-images-mongodb/">Upload/store images in MongoDB using Node.js, Express & Multer</a></li>
</ul>

Using the code base above, we put the Nodejs project in <strong>bezkoder-app</strong> folder and modify some files to work with environment variables.

Firstly, let's add <code>dotenv</code> module into <em>package.json</em>.

```json
{
  ...
  "dependencies": {
    "dotenv": "^10.0.0",
    ...
  }
}
```

Next we import <code>dotenv</code> in <em>server.js</em> and use <code>process.env</code> for setting port.

```js
require("dotenv").config();
..
// set port, listen for requests
const PORT = process.env.NODE_DOCKER_PORT || 8080;
app.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}.`);
});
```

Then we change modify database configuration and initialization.

<strong>app</strong>/<strong>config</strong>/<em>db.config.js</em>
```js
const {
  DB_USER,
  DB_PASSWORD,
  DB_HOST,
  DB_PORT,
  DB_NAME,
} = process.env;

module.exports = {
  url: `mongodb://${DB_USER}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?authSource=admin`
};
```

We also need to make a <em>.env</em> sample file that shows all necessary arguments.

<strong>bezkoder-app</strong>/<em>.env.sample</em>
```properties
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=123456
DB_NAME=bezkoder_db
DB_PORT=27017

NODE_DOCKER_PORT=8080
```

<h2>Create Dockerfile for Nodejs App</h2>
Dockerfile defines a list of commands that Docker uses for setting up the Node.js application environment. So we put the file in <strong>bezkoder-app</strong> folder.

Because we will use Docker Compose, we won't define all the configuration commands in this Dockerfile.

<strong>bezkoder-app</strong>/<em>Dockerfile</em>
```properties
FROM node:14

WORKDIR /bezkoder-app
COPY package.json .
RUN npm install
COPY . .
CMD npm start
```

Let me explain some points:
<ul>
	<li><code>FROM</code>: install the image of the Node.js version.</li>
	<li><code>WORKDIR</code>: path of the working directory.</li>
	<li><code>COPY</code>: copy <em>package.json</em> file to the container, then the second one copies all the files inside the project directory.</li>
	<li><code>RUN</code>: execute a command-line inside the container: <code>npm install</code> to install the dependencies in <em>package.json</em>.</li>
	<li><code>CMD</code>: run script <code>npm start</code> after the image is built.</li>
</ul>

<h2>Write Docker Compose configurations</h2>
On the root of the project directory, we're gonna create the <em>docker-compose.yml</em> file. Follow <a href="https://docs.docker.com/compose/compose-file/compose-file-v3/">version 3</a> syntax defined by Docker:

```properties
version: '3.8'

services: 
    mongodb:
    app:

volumes:
```
<ul>
	<li><code>version</code>: Docker Compose file format version will be used.</li>
	<li><code>services</code>: individual services in isolated containers. Our application has two services: <code>app</code> (Nodejs) and <code>mongodb</code> (MongoDB database).</li>
	<li><code><a href="https://docs.docker.com/storage/volumes/">volumes</a></code>: named volumes that keeps our data alive after restart.</li>
</ul>

Let's implement the details.

<em>docker-compose.yml</em>
```properties
version: "3.8"

services:
  mongodb:
    image: mongo:5.0.2
    restart: unless-stopped
    env_file: ./.env
    environment:
      - MONGO_INITDB_ROOT_USERNAME=$MONGODB_USER
      - MONGO_INITDB_ROOT_PASSWORD=$MONGODB_PASSWORD
    ports:
      - $MONGODB_LOCAL_PORT:$MONGODB_DOCKER_PORT
    volumes:
      - db:/data/db
  app:
    depends_on:
      - mongodb
    build: ./bezkoder-app
    restart: unless-stopped
    env_file: ./.env
    ports:
      - $NODE_LOCAL_PORT:$NODE_DOCKER_PORT
    environment:
      - DB_HOST=mongodb
      - DB_USER=$MONGODB_USER
      - DB_PASSWORD=$MONGODB_PASSWORD
      - DB_NAME=$MONGODB_DATABASE
      - DB_PORT=$MONGODB_DOCKER_PORT
    stdin_open: true
    tty: true

volumes:
  db:
```

- <strong>mongodb</strong>:
<ul>
	<li><code>image</code>: official Docker image</li>
	<li><code>restart</code>: configure the <a href="https://docs.docker.com/config/containers/start-containers-automatically/#use-a-restart-policy">restart policy</a></li>
	<li><code>env_file</code>: specify our <em>.env</em> path that we will create later</li>
	<li><code>environment</code>: provide setting using environment variables</li>
	<li><code>ports</code>: specify ports will be used</li>
	<li><code>volumes</code>: map volume folders</li>
</ul>

- <strong>app</strong>:
<ul>
	<li><code><a href="https://docs.docker.com/compose/compose-file/compose-file-v3/#depends_on">depends_on</a></code>: dependency order, <strong>mongodb</strong> is started before <strong>app</strong></li>
	<li><code>build</code>: configuration options that are applied at build time that we defined in the <em>Dockerfile</em> with relative path</li>
	<li><code>environment</code>: environmental variables that Node application uses</li>
	<li><code>stdin_open</code> and <code>tty</code>: keep open the terminal after building container</li>
</ul>

You should note that the host port (<code>LOCAL_PORT</code>) and the container port (<code>DOCKER_PORT</code>) is different. Networked service-to-service communication uses the container port, and the outside uses the host port.

<h2>Docker Compose Environment variables with MongoDB</h2>
In the service configuration, we used environmental variables defined inside the <em>.env</em> file. Now we start writing it.

<em>.env</em>
```properties
MONGODB_USER=root
MONGODB_PASSWORD=123456
MONGODB_DATABASE=bezkoder_db
MONGODB_LOCAL_PORT=7017
MONGODB_DOCKER_PORT=27017

NODE_LOCAL_PORT=6868
NODE_DOCKER_PORT=8080
```

<h2>Run Nodejs MongoDB with Docker Compose</h2>
We can easily run the whole with only a single command:
<code>docker compose up</code>

Docker will pull the MongoDB and Node.js images (if our machine does not have it before).

The services can be run on the background with command:
<code>docker compose up -d</code>

```bash
$ docker compose up -d
Creating network "node-mongodb_default" with the default driver
Creating volume "node-mongodb_db" with default driver
Pulling mongodb (mongo:5.0.2)...
5.0.2: Pulling from library/mongo
16ec32c2132b: Pull complete
6335cf672677: Pull complete
cbc70ccc8ebe: Pull complete
0d1a3c6bd417: Pull complete
960f3b9b27d3: Pull complete
aff995a136b4: Pull complete
4249be7550a8: Pull complete
cc105ff5aa3c: Pull complete
82819807d07a: Pull complete
81447d2c233f: Pull complete
Digest: sha256:54d24682d00278f64bf21ff62b7ee62b59dae50f65139831a884b345922b0f8a
Status: Downloaded newer image for mongo:5.0.2
Building app
Sending build context to Docker daemon  19.46kB
Step 1/6 : FROM node:14
14: Pulling from library/node
eb18d230e067: Pull complete 
83600c1b4583: Pull complete 
4ae15c65bfa0: Pull complete 
c19c058edda5: Pull complete 
05cdaa0fd103: Pull complete 
8461dcff50c4: Pull complete 
84be4117f79d: Pull complete 
4ccb803887fd: Pull complete 
ab52680a5469: Pull complete 
Digest: sha256:c1fa7759eeff3f33ba08ff600ffaca4558954722a4345653ed1a0d87dffed9aa
Status: Downloaded newer image for node:14
 ---> 256d6360f157
Step 2/6 : WORKDIR /bezkoder-app
 ---> Running in 71b4b2e9dd6c
Removing intermediate container 71b4b2e9dd6c
 ---> 194372d3695c
Step 3/6 : COPY package.json .
 ---> 093f866b404a
Step 4/6 : RUN npm install
 ---> Running in 025f0f0365a9
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN node-express-mongodb@1.0.0 No repository field.

added 81 packages from 128 contributors and audited 81 packages in 6.902s

2 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

Removing intermediate container 025f0f0365a9
 ---> 2f04aeaa93b1
Step 5/6 : COPY . .
 ---> 50e31a045a02
Step 6/6 : CMD npm start
 ---> Running in 7353ac17fa02
Removing intermediate container 7353ac17fa02
 ---> bd9d66054ea2
Successfully built bd9d66054ea2
Successfully tagged node-mongodb_app:latest
WARNING: Image for service app was built because it did not already exist. To rebuild this image you must use `docker compose build` or `docker compose up --build`.
Creating node-mongodb_mongodb_1 ... done
Creating node-mongodb_app_1     ... done
```

Now you can check the current working containers:

```bash
$ docker ps
CONTAINER ID   IMAGE              COMMAND                  CREATED         STATUS         PORTS                                         NAMES
42b9271dd73f   node-mongodb_app   "docker-entrypoint.s…"   2 minutes ago   Up 2 minutes   0.0.0.0:6868->8080/tcp, :::6868->8080/tcp     node-mongodb_app_1
e17bf545c0ba   mongo:5.0.2        "docker-entrypoint.s…"   2 minutes ago   Up 2 minutes   0.0.0.0:7017->27017/tcp, :::7017->27017/tcp   node-mongodb_mongodb_1
```

And Docker images:

```bash
$ docker images
REPOSITORY            TAG            IMAGE ID       CREATED         SIZE
node-mongodb_app      latest         bd9d66054ea2   5 minutes ago   960MB
node                  14             256d6360f157   6 minutes ago   944MB
mongo                 5.0.2          269b735e72cb   6 minutes ago   682MB
```

Let's send an HTTP request to check the Express Rest API:

<img src="https://www.bezkoder.com/wp-content/uploads/2021/08/docker-compose-nodejs-mongodb-example-test-api.png" alt="docker-compose-nodejs-mongodb-example-test-api" width="510" height="410" class="alignnone size-full wp-image-9124" />

And the MongoDB database also:

<img src="https://www.bezkoder.com/wp-content/uploads/2021/08/docker-compose-nodejs-mongodb-example-test-database.png" alt="docker-compose-nodejs-mongodb-example-test-database" width="600" height="390" class="alignnone size-full wp-image-9125" />

<h2>Stop the Application</h2>
Stopping all the running containers is also simple with a single command:
<code>docker compose down</code>

```bash
$ docker compose down
Stopping node-mongodb_app_1     ... done
Stopping node-mongodb_mongodb_1 ... done
Removing node-mongodb_app_1     ... done
Removing node-mongodb_mongodb_1 ... done
Removing network node-mongodb_default
```

If you need to stop and remove all containers, networks, and all images used by any service in <em>docker-compose.yml</em> file, use the command:
<code>docker compose down --rmi all</code>

```bash
$ docker compose down --rmi all
Stopping node-mongodb_app_1     ... done
Stopping node-mongodb_mongodb_1 ... done
Removing node-mongodb_app_1     ... done
Removing node-mongodb_mongodb_1 ... done
Removing network node-mongodb_default
Removing image mongo:5.0.2
Removing image node-mongodb_app
```

<h2>Conclusion</h2>
Today we've successfully created Docker Compose file for Nodejs and MongoDB application. Now we can deploy Nodejs Express and MongoDB with Docker on a very simple way: <em>docker-compose.yml</em>.

You can apply this way to one of following project:
<ul>
	<li><a href="https://bezkoder.com/node-express-mongodb-crud-rest-api/">Node.js Express Rest API with MongoDb</a></li>
	<li><a href="https://bezkoder.com/node-js-mongodb-pagination/">Node.js Express Pagination with Mongoose and MongoDB</a></li>
	<li><a href="https://bezkoder.com/node-js-mongodb-auth-jwt/">Node.js + MongoDB: User Authentication & Authorization</a></li>
	<li><a href="https://bezkoder.com/node-js-upload-store-images-mongodb/">Upload/store images in MongoDB using Node.js, Express & Multer</a></li>
</ul>

Or Dockerize fullstack:
- <a href="https://www.bezkoder.com/docker-mern/">Docker Compose: React + Node.js Express + MongoDB</a>

Happy Learning! See you again.

<h2>Source Code</h2>
The source code for this tutorial can be found at <a href="https://github.com/bezkoder/docker-compose-nodejs-mongodb">Github</a>.

<h2>Further Reading</h2>
Associations:
<ul>
	<li><a href="https://bezkoder.com/mongoose-one-to-one-relationship-example/">MongoDB One-to-One relationship tutorial with Mongoose example</a></li>
	<li><a href="https://bezkoder.com/mongoose-one-to-many-relationship/">MongoDB One-to-Many Relationship tutorial with Mongoose examples</a></li>
	<li><a href="https://bezkoder.com/mongodb-many-to-many-mongoose/">MongoDB Many-to-Many Relationship with Mongoose examples</a></li>
</ul>