How to Deploy a Multi-Container React.js and Node.js Application Using Docker Compose

325 VIEWS

· · · · · ·

Docker Compose is a powerful Docker tool for developing and running multi-container Dockerized applications. Docker is an open-source platform for developing and running applications in an isolated environment known as containers. A container is a standalone executable package that contains the libraries, source code, and dependencies needed to run an application.

With Docker Compose, you create a `docker-compose.yml` file to run all the containers in your project as a single application. The file will contain all the container configurations, such as volumes, container names, and port mappings. It also specifies the Dockerfiles for building the Docker images.

In this tutorial, we will create a backend Express server using Node.js. We will create a front-end application using React.js and connect it to the backend server. Using Docker Compose we will deploy the two applications. We will also access them on the web browser. Let’s start working on our applications!

Prerequisites

This tutorial assumes you are familiar with the following:

To implement this tutorial, you will need the following already installed on your computer:

Creating the Backend Express Server using Node.js

To create the Express server using Node.js, follow these steps:

Step 1: Create a working directory named `docker-compose-app` and open it with VS Code. 

In the working directory, create a new directory/folder named `node` and `cd` into the `node` directory. This directory will contain the libraries, source code, and dependencies needed to create the application. 

Step 2: Initialize the application using the following code:

npm init --y

The command will initialize the application and generate the `package.json` file. The next step is to install all the dependencies for the application as follows:

npm i -g nodemon
npm i express

After running the command in your terminal, it will install `express` and `nodemon`. We will use the installed `express` package to create the server.

We will use `nodemon` to watch and monitor the backend files. Nodemon will detect changes in the backend files and automatically restart the server. It will prevent us from restarting the backend server manually after making changes to the application.

Step 3: Next, open the `package.json` file and the following `npm` script for `nodemon` to start working:

"dev": "nodemon -L app.js"

Step 4: In the `node` directory, create a new file named `server.js`. This file will have the logic for generating our backend server.

Step 5: Copy and paste the following code snippet in the `server.js` file:

const express = require('express')
const cors = require('cors')

const app = express()

app.use(cors())

app.get('/', (req, res) => {
  res.json([
    {
      "id":"1",
      "title":"Album Review: When we all Fall asleep where do we go?"
    },
    {
      "id":"2",
      "title":"Book Review: How can we escape this labyrinth of suffering?"
    },
    {
      "id":"3",
      "title":"Documentary Review: How can we escape the rat race?"
    }
  ])
})

app.listen(4000, () => {
  console.log('connected on port 4000')
})

The code snippet above will create a `get` route. The frontend application will get the review titles from this route. 

Running the Backend Express Server

To run the Backend Express Server application, run the following `npm` command in your terminal:

npm run dev

The `npm` command will start and run our server on `http://localhost:4000/`as shown in the image below:

The image shows the backend is running and displaying the reviews. Let’s start working on our frontend React.js application.

Creating the frontend React.js application

To create the frontend React.js application, follow the steps illustrated below:

Step 1: In the `docker-compose-app` working directory, run the following `npx` command to create a boilerplate for a React.js application.

npx create-react-app react

The `npx` command above will create a new directory named `react` in the `docker-compose-app` working directory. Now, `cd` into the `react` directory.

Step 2: Navigate inside the `react` directory, and open the `src` directory.

Step 3: While in the `src` directory, open the `App.js` file and add the following code snippet:

import { useEffect, useState } from 'react'
import './App.css';

function App() {
  const [reviews, setReviews] = useState([])
  useEffect(() => {
    fetch('http://localhost:4000/')
      .then(res => res.json())
      .then(data => setReviews(data))
  }, [])

  return (
    <div className="App">
      <header className="App-header">
        <h1>all Reviews</h1>
        {reviews && reviews.map(blog => (
          <div key={blog.id}>{blog.title}</div>
        ))}
      </header>
    </div>
  );
}

export default App;

The added code snippet will create a frontend react application. It will `fetch` the review titles from this backend `get` route. Our backend application is running on `http://localhost:4000/`. The next step is to run the frontend React.js application as shown in the next section.

Running the frontend React.js application

To run the frontend React.js application, execute the following command in your terminal:

npm start

The `npm` command will start and run the frontend React.js application on `http://localhost:3000/`as shown in the image below:

The image shows the frontend React.js application running and fetching the review titles from the backend. Let’s create the Dockerfiles for the two applications. Docker Compose will use the Dockerfiles to build and run the containers.

Create the Dockerfile for the Backend Express Server

The Dockerfile will contain all the commands for building the Docker image for the backend server application. While in the `node` directory/folder, create a new file named `Dockerfile`. In the created `Dockerfile` add the following commands to create a Docker image.

# It uses node:18-alpine as the base image for the Node.js application
FROM node:18-alpine

# It installs the nodemon package globally for monitoring and watching the backend Express server
RUN npm install -g nodemon

# Creating the working directory named `app`
WORKDIR /app

# Copying all the tools and dependencies in the package.json file to the working directory `app`
COPY package.json .

#Installing all the tools and dependencies in the container
RUN npm install

#Copying all the application source code and files to the working directory `app`
COPY . .

#Exposing the container to run on this port 4000
EXPOSE 4000

#Command to start the Docker container for the backed server application
CMD ["npm", "run", "dev"]

The next step is to create a `Dockerfile` for the frontend React.js application.

Create the Dockerfile for the frontend React.js application

In the `react` directory/folder, create a new file named `Dockerfile`. In the created `Dockerfile` add the following commands to create a Docker image.

# It uses node:18-alpine as the base image for the frontend React.js application
FROM node:18-alpine

# Creating the working directory named `app`
WORKDIR /app

# Copying all the tools and dependencies in the package.json file to the working directory `app`
COPY package.json .

#Installing all the tools and dependencies in the container
RUN npm install

#Copying all the application source code and files to the working directory `app`
COPY . .

#Exposing the container to run on this port 3000
EXPOSE 3000

#Command to start the Docker container for the frontend React.js application
CMD ["npm", "start"]

Now that we have both Dockerfiles, the next step is to create a `docker-compose.yml` file that contains all the containers’ configurations.

Create a `docker-compose.yml` file

This file will enable us to run and deploy the two containers using Docker Compose. We will add the containers as a `service`. 

The `docker-compose.yml` file will have two services. Using the created file, we will spin up the two containers. It will run them as a single application.

To create the two Docker Compose services, follow the steps below:

Step 1: In the `docker-compose-app` working directory, create a new file `docker-compose.yml` file.

Step 2: Next, add the first service named `node` using the following code snippet:

# Version of Docker-compose
version: '3.9'
services:
  # Add the node-js service
  node:
  # Location to the node.js dockerfile
    build: 
      context: ./node
        # Name of the dockerfile
      dockerfile: Dockerfile
    container_name: node-container
    ports:
       # Host port:Container port
      - '4000:4000'
    volumes:
      # Bind-mounts configuration
      - ./node:/app
      # Ignoring any changes made in the "node_modules" folder
      - ./app/node_modules

The code above will create a service named `node`. It also shows the location of the Node.js Dockerfile. 

It shows the container name as `node-container` and port mapping. The `node-container` will run on port `4000`.

We also add volume for this service. This volume will map the local project folder to the working directory in the container. Any changes made in the local project folder will be updated automatically in the container. It saves time rebuilding the whole container from scratch when change the files in the local project folder.

Step 3: Next, let’s add the service named `react` for the React.js application container.

After the `node` service, add the following code snippet:

react:
  # Location to the react.js dockerfile
    build: 
      context: ./react
        # Name of the dockerfile
      dockerfile: Dockerfile
    container_name: react-container
    ports:
     # Host port:Container port
      - '3000:3000'
    stdin_open: true

The code above will create a service named `react`. It also shows the location of the React.js Dockerfile. 

It shows the container name as `react-container` and port mapping. The `react-container` will run on port `3000`. 

If you follow the steps above correctly, the final `docker-compose.yml` will be as shown below:

# Version of Docker-compose
version: '3.9'
services:
  # Add the node-js service
  node:
  # Location to the node.js dockerfile
    build: 
      context: ./node
        # Name of the dockerfile
      dockerfile: Dockerfile
    container_name: node-container
    ports:
       # Host port:Container port
      - '4000:4000'
    volumes:
      # Bind-mounts configuration
      - ./node:/app
      # Ignoring any changes made in "node_modules" folder
      - ./app/node_modules
  react:
  # Location to the react.js dockerfile
    build: 
      context: ./react
        # Name of the dockerfile
      dockerfile: Dockerfile
    container_name: react-container
    ports:
     # Host port:Container port
      - '3000:3000'
    stdin_open: true

Now that we have added all our services to the file, the next step is to build and run the two containers.

Running the two Containers using Docker Compose

To build and run the two Containers using Docker Compose, execute the following command in your terminal:

docker-compose up --build

The command will run the two containers and display the following output in your terminal:

  • `node-container`

  • `react-container`

Conclusion

In this tutorial, you have learned how to deploy a multi-container React.js and Node.js Application using Docker Compose. We created a backend Express server using Node.js. We then created a front-end application using React.js. After completing these steps, we created Dockerfiles for these two applications. We also created a `docker-compose.yml` file.

Using `docker-compose.yml` we built and ran the two application containers. We successfully deployed the Docker containers using Docker Compose. We accessed them on the web browser. You can download the complete source code for this tutorial here. Happy Deployment!


Bravin Wasike holds an undergraduate degree in Software Engineering. He is currently a freelance data scientist and machine learning engineer. He is passionate about machine learning and deploying models to production using Docker and Kubernetes. Bravin also loves writing articles on machine learning and various advances in the technological industry. He spends most of his time doing research and learning new skills in order to solve different problems.


Discussion

Click on a tab to select how you'd like to leave your comment

Leave a Comment

Your email address will not be published. Required fields are marked *

Menu
Skip to toolbar