How to Deploy a Multi-Container React.js and Node.js Application With Docker Compose
Tags: webdev, javascript, programming, devops
Published: 2023-07-19 08:46:55

[Docker Compose](https://docs.docker.com/compose/) is a powerful [Docker](https://docs.docker.com/get-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 a container. 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](https://expressjs.com/) server using Node.js. We will then create a front-end application using React.js and connect it to the backend server. 

We will then use Docker Compose to 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:
- [React.js](https://reactjs.org/)
- [Node.js](https://nodejs.org/)
- [Docker](https://docs.docker.com/get-docker/)

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

- [VS Code](https://code.visualstudio.com/Download) code editor.
- [Node.js](https://nodejs.org/) framework.
- [Docker Desktop](https://www.docker.com/products/docker-desktop).

### Creating the Backend Express Server using Node.js
To create the Express server using Node.js, follow the steps illustrated below:

**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
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
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:

```npm 
"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 creating our backend server.

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

```node
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
npm run dev
```
The `npm` command will start and run our server on `http://localhost:4000/`as shown in the image below:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rp05f3flhg28c26pm758.png)

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

```npm
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:

```react
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 follows:

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

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

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2e1zidcpi2zvgwh2ywgh.png)

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.

```docker
# 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.

```docker
# 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 which 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:

```docker
# 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:

```docker
 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:

```docker
# 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
docker-compose up --build
```
The command will run the two containers and display the following output in your terminal:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mqs1gj8nz5zlth74habk.png)

The output shows both the `node-container` and `react-container` are running. We can access the `node-container` on `http://localhost:4000/` and `react-container` on `http://localhost:3000/`. The images below show the deployed containers running in the web browser:

- `node-container`
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ll5xy82vztv6iib3xpu2.png)

- `react-container`
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/artgurwu2dkn43bwuace.png)

### 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](https://expressjs.com/) 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.

We used the `docker-compose.yml` to build and run 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](https://github.com/Bravinsimiyu/react-node-app). Happy Deployment!