Dockerizing a Node.js Application with MongoDB
Published: 2024-06-28 21:22:07

In this blog post, I will guide you through Dockerizing a Node.js application that uses MongoDB. We will cover setting up the Node.js app, creating a MongoDB database, and using Docker to containerize both services. By the end of this tutorial, you'll have a working application running in Docker containers.

## Prerequisites
- Basic knowledge of Node.js and Express
- Basic understanding of Docker and Docker Compose
- Node.js and Docker installed on your machine

## Project Structure

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6vm309xqgajbu7wb48ko.PNG)

## Step 1: Setting Up the Node.js Application

First, let's set up our Node.js application. We'll create a simple product management API using Express and MongoDB. We'll use the src folder for this in our project.

### `ProductController.js`
This file contains the controller logic for handling requests related to products.
```javascript
import Product from '../models/Product.js';

async function index(req, res, next) {
    try {
        const products = await Product.find();
        return res.status(200).json({
            status: 200,
            data: products
        });
    } catch (e) {
        console.log(e);
        return res.status(500).json({
            message: e
        });
    }
}

async function save(req, res, next) {
    try {
        const product = new Product();
        product.title = req.body.title;
        product.description = req.body.description;
        product.price = req.body.price;
        await product.save();

        return res.status(201).json({
            status: 201
        });
    } catch (e) {
        console.log(e);
        return res.status(500).json({
            message: e
        });
    }
}

export default {
    index,
    save
};
```

### `Product.js`
This file defines the Mongoose schema for our product model.
```javascript
import mongoose from 'mongoose';

const ProductSchema = new mongoose.Schema({
    title: {
        type: String,
        required: true
    },
    description: {
        type: String,
        required: true
    },
    price: {
        type: String,
        required: true
    }
});

export default mongoose.model('Product', ProductSchema);
```

### `products.js`
This file defines the routes for our product API.
```javascript
import express from 'express';
import productsController from '../controllers/ProductController.js';

const router = express.Router();

router.get('/', productsController.index);
router.post('/', productsController.save);

export default router;
```

### `app.js`
This file sets up our Express application and connects to MongoDB.
```javascript
import express from 'express';
import productRoutes from './routes/products.js';
import mongoose from 'mongoose';
import config from '../config.js';
import bodyParser from 'body-parser';

const connectToDB = async () => {
    try {
        await mongoose.connect(config.db_uri, {});
    } catch (e) {
        console.log(e);
        process.exit(1);
    }
};

const app = express();

// middleware
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());

app.use('/products', productRoutes);

await connectToDB();

export default app;
```

### `index.js`
This file starts the Express application.
```javascript
import app from './app.js';
import config from '../config.js';

app.listen(3000, () => {
    console.log(config.app_name + ' Started on Port 3000');
});
```
### `config.js`

```
import dotenv from 'dotenv';
import mongoose from "mongoose";

dotenv.config();

const config = {
    app_name: process.env['APP_NAME'],
    port: process.env['PORT'] ?? 3000,
    db_uri: process.env['DB_URI'] ?? 'mongodb://localhost:27017/docker',
    db_options: {
        useNewUrlParser: true,
        useUnifiedTopology: true
    }
}
export default config;
```

### `env`

```
APP_NAME=LearnDocker
PORT=3000
DB_URI=mongodb://127.0.0.1:27017/dockerlearn #This is a local Mongo database
```

## Step 2: Dockerizing the Application

Now, let's create a Dockerfile to containerize our Node.js application.

`Dockerfile`

```
# Use the official Node.js image based on Alpine Linux, which is a lightweight distribution
FROM node:alpine

# Set the working directory inside the container
WORKDIR /usr/src/app

# Copy package.json and package-lock.json to the working directory
COPY ./package.json ./
COPY ./package-lock.json ./

# Install the Node.js dependencies
RUN npm install

# Copy the application source code to the working directory
COPY ./src ./src

# Copy the environment configuration file
COPY ./.env ./

# Copy the configuration file
COPY ./config.js ./

# Define the command to run the application
CMD ["npm", "start"]
```


### Docker Compose
We'll use Docker Compose to set up our Node.js application and MongoDB as separate services.
`docker-compose.yml`
```
version: "3"

services:
  # Define the MongoDB service
  mongo_db:
    container_name: database_container  # Set a custom name for the MongoDB container
    image: mongo:latest  # Use the latest version of the MongoDB image
    restart: always  # Always restart the container if it stops or fails
    volumes:
      - mongo_db:/data/db  # Map the mongo_db volume to /data/db inside the container to persist data

  # Define the Node.js application service
  app:
    build: .  # Build the image from the Dockerfile in the current directory
    ports:
      - 4000:3000  # Map port 4000 on the host to port 3000 in the container
    environment:
      APP_NAME: LearnDocker  # Set the application name environment variable
      PORT: 3000  # Set the application port environment variable
      DB_URI: mongodb://mongo_db:27017/dockerlearn  # Set the MongoDB URI to connect to the MongoDB container
    depends_on:
      - mongo_db  # Ensure the mongo_db service is started before this service

volumes:
  mongo_db: {}  # Define a named volume for MongoDB data
```

## Step 3: Running the Application

To run the application, follow these steps:

1. **Build the Docker images**:
   ```bash
   docker-compose build
   ```

2. **Start the services**:
   ```bash
   docker-compose up
   ```

You can test the application on Postman at the endpoint localhost:4000/products

You should now see the product management API in action.

## Conclusion

Congratulations! You've successfully Dockerized a Node.js application with MongoDB. This setup provides a clean and efficient way to manage your application and its dependencies. Feel free to expand this project further and explore more features of Docker and Node.js.