Dockerize a React app with Node.js backend connected to MongoDb
Tags: react, node, docker, javascript
Published: 2020-01-05 13:06:57

---
title: Dockerize a React app with Node.js backend connected to MongoDb
published: true
description: 
tags: react, nodejs, docker, javascript
cover_image: https://images.unsplash.com/photo-1444211111734-7cddabac4149?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1053&q=80
series: docker
---

Hello dear coder, welcome to my last tech article of series dedicated to *Node.js* and *Docker*. Hope you enjoy!

## Problem:
We already now how to use Docker together with Node and Mongo from previous article in this series. In order to complete our MERN stack application we need to add frontend part. In our case, frontend will be implemented using React. Let's learn how to create full working application with frontend, backend, database and run everything in Docker! 

## 1. Clone backend Node.js
In previous [part of this series](https://dev.to/vguleaev/dockerize-a-node-js-app-connected-to-mongodb-5bp1) we created a Node.js app using MongoDb with Docker. For this tutorial we will need the same project. Clone [source code from here](https://github.com/vguleaev/Express-Mongo-Docker-tutorial) or run this command:

```
git clone https://github.com/vguleaev/Express-Mongo-Docker-tutorial.git
```
After cloning is done, rename folder from `test-mongo-app` to `api`. This will be our backend. 

To test that everything works, open `api` folder and run `npm install`. After dependencies are installed, let's check if everything works. 🍾

```
docker-compose up
```

This command will use our `docker-compose.yml` to pull mongo image and start express server connected to MongoDb.

If everything is ok you should see in console something like this:
```
web_1    | Listening on 8080
web_1    | MongoDb connected
```
Open in browser this endpoint http://localhost:8080/users and you should get an empty array as response. Which is correct because our database is completely empty for now.

## 2. Create React app
Time to develop our frontend part. Go up to parent directory and run:
```
npm i create-react-app -g
create-react-app ui
```
Right now our folder structure should look like this:
**...**
├── / api
└── / ui
(*Where api is cloned backend app and ui is newly created React app.*)

To be sure that everything works let's open **ui** folder and start React app:
```
cd ui
npm start
```
You should see basic React app at http://localhost:3000. 🎈

## 3. Dockerize React app
In **ui** folder create a `.dockeringore` file:

```docker
node_modules
.git
.gitignore
```
*(Without this file, our `docker build` command will be just hanging on Windows.)*

Also create a `Dockerfile` file in **ui** folder:
```docker
FROM node:8
# Create app directory
WORKDIR /usr/src/app
# Install app dependencies
COPY package*.json ./

RUN npm install --silent
# Copy app source code
COPY . .

#Expose port and start application
EXPOSE 3000
CMD ["npm", "start"]
```

Let's test that React works in docker. First we will build the image with tag react:app:
```
docker build -t react:app .
```

Now run our tagged image and use the same port for docker:
```
docker run -p 3000:3000 react:app
```
Open http://localhost:3000 and you should see React served from Docker. 👍

⚠️ If you just close like you usually do with Ctrl+C container won't stop. To stop container from running do `docker ps` command.

```
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
06c982ce6ae9        react:app           "docker-entrypoint.s…"   12 days ago         Up About a minute   0.0.0.0:3000->3000/tcp   strange_montalcini
```

Then pick desired id and stop container.

```
docker stop 06c982ce6ae9
```

## 4. Call api from React app
Open **ui** folder and install `axios`

```
cd ui
npm i axios
```

We will change `App` component a bit to have a button to create users and show list of users ids. We will call */user-create* and */users* GET endpoints from our Nodejs app.

Paste this into App.js file:
```react
import React, { Component } from 'react';
import logo from './logo.svg';
import axios from 'axios';
import './App.css';

const apiUrl = `http://localhost:8080`;

class App extends Component {
  state = {
    users: []
  };

  async createUser() {
    await axios.get(apiUrl + '/user-create');
    this.loadUsers();
  }

  async loadUsers() {
    const res = await axios.get(apiUrl + '/users');
    this.setState({
      users: res.data
    });
  }

  componentDidMount() {
    this.loadUsers();
  }

  render() {
    return (
      <div className="App">
        <header className="App-header">
          <img src={logo} className="App-logo" alt="logo" />
          <button onClick={() => this.createUser()}>Create User</button>
          <p>Users list:</p>
          <ul>
            {this.state.users.map(user => (
              <li key={user._id}>id: {user._id}</li>
            ))}
          </ul>
        </header>
      </div>
    );
  }
}

export default App;
```

Since we run frontend on port 3000 but backend is running on port 8080 we are going to have a [CORS](https://en.wikipedia.org/wiki/Cross-origin_resource_sharing) problem. To avoid it go to **api** project and install cors package.
```
npm i cors
```

Then use it in `server.js` file:

```javascript
const express = require('express');
const app = express();
const connectDb = require('./src/connection');
const User = require('./src/User.model');
const cors = require('cors');

app.use(cors());
// ...
```

## 5. Run React and Node together in Docker

Final step! Now remove `docker-compose.yml` from directory **api** and create `docker-compose.yml` in root folder. Paste this:

```yml
version: '2'
services:
  ui:
    build: ./ui
    ports:
      - '3000:3000'
    depends_on:
      - api
  api:
    build: ./api
    ports:
      - '8080:8080'
    depends_on:
      - mongo
  mongo:
    image: mongo
    ports:
      - '27017:27017'

```

Our root folder structure now looks like this:
**...**
├── / api
├── / ui
└── docker-compose.yml

We have one docker-compose that describes what services we want to run in Docker. In our case we have three services: **ui, api, mongo**. 🐋

For each service will be created docker image using `Dockerfile` in each project. We specify the path in line build. (e.g. `build: ./ui`) 

For **mongo** we don't have project to build image, because we use predefined image from docker [hub](https://hub.docker.com/_/mongo/). (e.g. `image: mongo`)

We also specify ports and dependencies. In our case first will be started **mongo** on port *27017*, because api depends on mongo. Second container is **api** on port *8080* because **ui** depends on it. Last container is **ui** which starts on port *3000*.

Finally from root folder run all services with one command! 🧙
```
docker-compose up --build
```
Open http://localhost:3000/ and click on button to create users. Open Developer tools to have a look at calls. Now we run both frontend and backend from docker!

![Alt Text](https://thepracticaldev.s3.amazonaws.com/i/6i9cp41rpm88fj1ftc71.PNG)

## 6. Use React production build

Right now we start our React app with development server which is probably not what we want to use in production. But we can easy fix this problem.

We simply need to change our `Dockerfile` in **ui** project. We will start a production build and serve it using nginx server. Replace everything with this:

```docker
# build environment
FROM node:12.2.0-alpine as build
WORKDIR /app
ENV PATH /app/node_modules/.bin:$PATH
COPY package.json /app/package.json
RUN npm install --silent
RUN npm install react-scripts@3.0.1 -g --silent
COPY . /app
RUN npm run build

# production environment
FROM nginx:1.16.0-alpine
COPY --from=build /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```

Since we now expose port 80, we need to change it from 3000 to 80 in `docker-compose.yml`.
```yml
  ui:
    build: ./ui
    ports:
      - '80:80'
    depends_on:
      - api
```

Now run again magic command to start everything in docker 🔮
```
docker-compose up --build
```

Open http://localhost/ and you should see exactly the same working application but now React is running in production mode.

See the [source code here](https://github.com/vguleaev/React-Express-Mongo-Docker-tutorial/tree/master/react-express-mongo-app). Enjoy!

Congratulation you successfully dockerized React, Nodejs and Mongodb! 🎉🎉🎉

<hr/>

🚀 If you read something interesting from that article, please like and follow me for more posts. Thank you dear coder! 😏