Dockerize monolithic apps — Part 1

Aashi Singh
4 min readApr 15, 2023

--

Recently, I wanted to implement docker for an app, which I developed using Node.js and Angular. But the catch here is, both the frontend and backend resides in the same folder. Of course, it would have been easier to divide them in separate repositories and then dockerize each of them. But I decided not to change the structure as the app itself was a very basic one — with limited functionalities and I didn’t want to maintain two different repositories and host them on different servers. You might have also encountered such problems especially when working with a legacy monolithic app. So, let’s try to implement docker together!

PREREQUISITES

  1. Node.js and NPM— https://docs.npmjs.com/downloading-and-installing-node-js-and-npm
  2. Postgresql — https://www.postgresql.org/download
  3. Docker — https://docs.docker.com/get-docker

I developed a simple node-angular app, which pulls fake employee data from a json and shows it in a tabular format. You can pull the code from the Github repository link. Create a .env file in the root directory of the project. The contents of this file are —

PORT=3000
ENV=development
URL=http://api.jsoneditoronline.org/v1/docs/088b6155ffc749e9a8ace0721f2cc384/data
SESSION_SECRET=psyduck
DB_USER=postgres
DB_PASSWORD=root

Note: Replace the values of DB_USER and DB_PASSWORD as per your system values.

Follow these commands to run the project in your system —

npm install
npm run migrate
npm start

Note: npm run migrate will create the database in your system. More information on how this command works will be added in the next part of this series — Coming Soon!

Open localhost:3000 in your browser to see the project up and running.

Note: If you want to see the live changes for the frontend, run the command npm run serve in a separate terminal and open localhost:4200.

Project running in localhost:3000

Adding docker to the app

Note: To add docker, make sure that docker is installed in your system. If not, follow this link to install it.

Add Dockerfile to the root directory of the project. This contains all the commands to be called on the command line to assemble an image.

FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
CMD [ "npm", "run", "start" ]
EXPOSE 3000

This will host our server on 3000 port, but the main challenge is to run the frontend of the app as well to see live changes made in the code. To make this work, let’s add another Dockerfile named as Dockerfileclient and rename the above file as Dockerfileserver.

FROM node:16-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 4200
CMD [ "npm", "run", "serve" ]

This hosts the client on 4200 port. docker-compose.yaml looks like this —

services:
server:
build:
context: ./
dockerfile: Dockerfileserver
volumes:
- .:/app
- /app/node_modules
restart: always
ports:
- "3000:3000"
depends_on:
- client
client:
build:
context: ./
dockerfile: Dockerfileclient
volumes:
- .:/app
- /app/node_modules
restart: always
ports:
- "4200:4200"

Here, we clarify which Dockerfile to use while starting the server or client service. Run these commands to have the docker image up and running.

Note: Make sure to run docker desktop before running these commands.

docker-compose build
docker-compose up

Now, if you go to localhost:4200 to run the app, we see the front end is loaded but we are not able to get any data from the backend. This is weird. Let’s debug it further!

Before dockers, the frontend and backend of the apps were running on different ports — 4200 and 3000 respectively. But how were they communicating with each other? The answer lies in the file — proxy.conf.json. This tells the frontend that all the api requests to the server should be proxied to localhost:3000.

{
"/api": {
"target": "http://localhost:3000/",
"secure": false
}
}

But, while using dockers, we have two containers — server and client. Instead of proxying our requests to localhost:3000, we need to proxy it through the server container. So just make this change to the proxy.conf.json file and build the image again.

{
"/api": {
"target": "http://server:3000/",
"secure": false
}
}

Voila! Our app is back to running again.

Project running through docker, on localhost:3000

There is one problem here — if we make any change to the frontend, it reflects in the app on a reload. But changes in the server need a restart of the app. To tackle this, lets use nodemon.

Install the library using — npm install nodemon. Now, add the script in package.json to start the app using nodemon —

"dev": "nodemon --ignore 'dist/*' server.js"

Update the Dockerfileserver to use npm run dev instead of npm run start. Now the app should reload on any changes to the server.

To stop the docker container, use the command docker-compose down.

In the next part of this blog, we will discuss on how to add database to this app and make it work with dockers! Check the Part 2 of the series here.

I hope you found this post useful and please do let me know in case of any question or query. Leave a comment here and I will surely reply!

--

--

Aashi Singh
Aashi Singh

Written by Aashi Singh

Full Stack Developer at Salesforce | Creative coder | JavaScript Nerd