loading...
Cover image for Docker-compose an express and mongo App

Docker-compose an express and mongo App

jay97 profile image Jamal Al ・5 min read

Docker is a Tool that enables you to containerize your dev environment and by so helps you be more productive to focuses more on the code and stop worrying about setting up your environment to match all your teammate's workspace.

Docker-compose is another tool that works with docker to group containers or services to work together as one App.

you need to install Docker and Docker-compose to follow

First, write a Simple express server

index.js

const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const app = require('express')();


app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({ extended: false }));

app.get('/', (req, res) =>{
    res.send("it's working :)")   
})

const port = 3000;
app.listen(port, () => console.log('Server running...'));

In the above code, we require a couple of modules that we will work with later. And write a small server to listen for GET / route.

Next let's add database connection and write the schema

index.js

const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const app = require('express')();


// connect to Mongo daemon
mongoose
  .connect(
    'mongodb://localhost/express-mongo',
    { useNewUrlParser: true }
  )
  .then(() => console.log('MongoDB Connected'))
  .catch(err => console.log(err));


// DB schema
const ItemSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true
  },
  date: {
    type: Date,
    default: Date.now
  }
});

Item = mongoose.model('item', ItemSchema);
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({ extended: false }));

app.get('/', (req, res) =>{
    res.send("it's working :)")   
})


const port = 3000;
app.listen(port, () => console.log('Server running...'));

changes

  1. added DB
  2. Added Schema for our app

Note: we're connecting to localhost for now but this will change as we bring the mongo Image from Docker Hub. If you don't have MongoDB installed it's ok you don't need to install anything.

Next let's make a simple ejs view

/views/index.ejs

<!DOCTYPE html>
<html lang="en">

<head>
  <title>My Node App</title>
</head>

<body>
  <h1>My Node App</h1>
  <form method="post" action="/item/add">
    <label for="name">Name</label>
    <input type="text" name="name">
    <input type="submit" value="Add">
  </form>
</body>

</html>

so, it's just a simple view that displays a form and submits the form to /item/add

Next we create the post route And render the View

index.js

const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const app = require('express')();


// connect to Mongo daemon
mongoose
  .connect(
    'mongodb://localhost/express-mongo',
    { useNewUrlParser: true }
  )
  .then(() => console.log('MongoDB Connected'))
  .catch(err => console.log(err));


// DB schema
const ItemSchema = new mongoose.Schema({
  name: {
    type: String,
    required: true
  },
  date: {
    type: Date,
    default: Date.now
  }
});

Item = mongoose.model('item', ItemSchema);
app.set('view engine', 'ejs');
app.use(bodyParser.urlencoded({ extended: false }));

app.get('/', (req, res) =>{
    res.render("index");   
})


//Post route
app.post('/item/add', (req, res) => {
  const newItem = new Item({
    name: req.body.name
  });

  newItem.save().then(item => res.redirect('/'));
});


const port = 3000;
app.listen(port, () => console.log('Server running...'));

Now, the first GET/ route will display the form and the POST/item/add route will take the date, create an item in DB and redirect back to /. An item can be anything really, we don't care as it's not the main point of this Post.
Also let's change our /view/index.ejs to show all items in DB.
/views/index.ejs

  <h4>names:</h4>
  <ul>
    <% items.forEach(function(item) { %>
    <li>
      <%= item.name %>
    </li>
    <% }); %>
  </ul>

Add this under the Form in index.ejs

Now let's dive into docker

First, let's make our Dockerfile to build a nodejs image out of the Dockerfile

/Dockerfile

FROM node:10 //pull a node image from docker hub

WORKDIR /app //set the working dir to /app

COPY package.json package.json //copy package.json to the container

RUN npm install // install package.json modules in container

COPY . . //copy everything to container /app

EXPOSE 3000 //expose port 3000 to mount it to another port in local machine 

RUN npm install -g nodemon // install nodemon for changes on the fly

CMD [ "nodemon", "index.js" ] // start server inside container
  1. The first line From pulls a nodejs image
  2. The second line set the working place inside the container
  3. Copy package.json to working dir inside the container
  4. Install package.json modules inside the container
  5. Copy all files in the current dir to workdir in the container
  6. Expose port 3000 to map it to some other port on our machine to access the app \
  7. Install nodemon inside the container
  8. Run the app

Next we need to create the docker compose

The docker-compose.yml will enable us to run multiple containers all together to build our app. So let's make a docker-compose.yml file.

/docker-compose.yml

version: "3"
services:
  app:
    container_name: express-mongo
    restart: always
    build: ./
    ports:
      - "80:3000"
    volumes:
      - .:/app
    links:
      - mongo
  mongo:
    container_name: mongo
    image: mongo
    ports:
      - "27017:27017"
  1. First Line tells docker what version of docker-compose to use(3 is the latest).
  2. The second line defines the services that we will have which in this case are (node container and mongo container)
  3. Line 3 defines the services name, it could be anything
  4. Line 4 defines the container name
  5. Line 5 tells docker container to restart if anything crash (notice this is only for nodejs container)
  6. Line 6 build the nodejs image from the Dockerfile that we created earlier.
  7. Line 7 mount port 3000 in the container to port 80 on our machine
  8. Line 8 bind all files in our local workdir to the workdir in the container to see updates and changes in real time.

  9. Line 9 links mongo container to node container to let them communicate together

  10. Line10 creates other services (mongo)

  11. Line 11 defines the container name

  12. Line 12 pulls mongo image from docker hub

  13. Line 13 expose port 27017 in mongo container to our local machine to be able to communicate with mongo

Next we need to change the path to connect to MongoDB

Change this
index.js

mongodb://localhost/express-mongo

To this
index.js

mongodb://mongo:27017/expressmongo

This will connect to the mongo container exposed port 27017

Now moment of truth

Run this command in your terminal
docker-compose up
//or 
docker-compose up -d //to run docker-compose in background

Now check your browser on port 80 for the app.

To stop it, hit ctl+c
If you have it runing in background type docker-compose stop to stop it.
To remove both containers run docker-compose down

Now you can push the project to github and pull it down from any machine and it will work out of the box as long as you have docker and docker-compose.

Note: when you pull the code from githup you need to install all node_modules by doing npm install because the container will look for express and all other modules inside node_modules on your machine. If you don't want to install any node_modules on your machine you need to add this to your docker-compose.yml

Under volumes add this

- /app/node_modules // now all you need to do is run docker-compose up

Posted on Sep 17 '18 by:

jay97 profile

Jamal Al

@jay97

Free individual, lost in the world of javascript

Discussion

markdown guide
 

Depending on your platform (I'm using CentOS), setting the ports property in the mongo section of the docker-compose.yml file will also open port 27017 on your firewall, which you may not want to do.

 

Exactly what I was looking for, thanks!

Maybe I missed something, but I think you forgot to mention that you need to fetch the items from the DB to display them in the index route.

Something like that:

app.get("/", (req, res) => {
  Item.find((err, items) => {
    if (err) throw err;
    res.render("index", { items });
  });
});
 

Glad it helped and you're absolutely correct. I'll fix it as soon as I get time

 

Hi Jamal, thank you for the nice and clear explanations. It would be great if you can add the package.json file to this post as well.

 

hey Enes K. Thanks for the nice comment.

make a file package.json in your project, copy/past bellow json object
and run npm install to get all the dependences.

{
    "name": "docker-demo",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts":
        "start": "nodemon index.js"
    },
    "keywords": [],
    "author": "Jamal",
    "license": "ISC",
    "dependencies": {
        "express": "^4.16.4",
        "mongoose": "^5.4.19",
        "ejs":"^"
    },
    "devDependencies": {
        "nodemon": "^1.18.10",

    }
}