Streamline Your Deployment Process: Deploying a React and Node App with GitHub Actions and AWS Elastic Beanstalk
Tags: github, automation, githubactions, devops
Published: 2023-10-18 14:12:43

In our previous blog, we delved deep into the YAML syntax, triggers, jobs, and steps that are crucial in defining powerful workflows with GitHub Actions. Building on that foundation, I am back with another exciting blog that will take your development workflows to new heights of automation and efficiency. 

Today, we will embark on a journey where we connect a `React` app with a `Node` app, `dockerize` the entire application stack, and deploy it seamlessly using the power of `GitHub Actions` and `AWS Beanstalk`. By the end of this blog, you will have a comprehensive understanding of how to orchestrate the deployment process effortlessly, regardless of the complexity of your application. Let's dive in and discover the magic of GitHub Actions with practical scenarios and industry-specific applications! Stay tuned and let's get started on this exciting journey. 🚀💻✨

# Prerequisites

1. Basic knowledge of react and node.js.
2. Basic knowledge of docker.
3. Basic knowledge of git and github.
4. My previous blog on github actions. [Read Here](https://dev.to/bhavesh_yadav/supercharge-your-cicd-building-powerful-workflows-with-github-actions-bp1)
5. Basic knowledge of AWS and how to setup an IAM user.(comment if you want me to write a full fledged blog on AWS IAM and Beanstalk)


## Set up the React app
- Make sure you have Node.js and npm installed on your machine.
- Open your command line interface and navigate to the directory where you want to create your React app.
- Run the following command to create a new React app:

```
npx create-react-app my-app
```

- Once the command completes, navigate into the `my-app` directory:

```
cd my-app
```

- Start the React development server by running:

```
npm start
```

- You should see your React app running locally at `http://localhost:3000`.

## Set up Node.js backend
- In the root of your React app (`my-app` directory), create a new directory called `server`.
- Navigate into the `server` directory and initialize a new Node.js project:

```
npm init -y
```

- Install the necessary dependencies. For example, you can use Express.js as the backend framework:

```
npm install express
```

- Create a new file in the `server` directory called `index.js` and add the following code:

```
const express = require('express');
const app = express();
const port = process.env.PORT || 5000;

app.get('/api', (req, res) => {
  res.json({ message: 'Backend API is working!' });
});

app.listen(port, () => {
  console.log(`Server is running on port ${port}`);
});
```

- Save the file and go back to the root of your React app.

## Connect the React app to the backend
- Open the `src/App.js` file in your React app.
- Replace the existing code with the following:

```
import { useState, useEffect } from 'react';

function App() {
  const [message, setMessage] = useState('');

  useEffect(() => {
    fetch('/api')
      .then(response => response.json())
      .then(data => setMessage(data.message));
  }, []);

  return (
    <div className="App">
      <h1>Hello, World!</h1>
      <p>{message}</p>
    </div>
  );
}

export default App;
```

- Save the file.

## Dockerize the application
- In the root of your React app, create a file called `Dockerfile`.
- Lets build a `Dockerfile` step by step:

```
# Base image
FROM node:14-alpine
```

- This specifies the base image for the Docker image. It sets the Node.js version to 14 and the Alpine Linux distribution as the base.

```
# Set working directory
WORKDIR /app
```

- This sets the working directory inside the Docker container to `/app`. It means that all subsequent commands and file operations will be performed relative to this directory.

```
# Copy package.json
COPY package.json ./
```

- This line copies the `package.json` file from the local directory (where the Dockerfile is located) to the `/app` directory inside the Docker image. These files contain information about the project's dependencies.

```
# Install dependencies
RUN npm install
```

- This line runs the command `npm install` inside the Docker image. With the working directory set to `/app`, it installs the dependencies defined in `package.json` using the Node Package Manager (NPM).

```
COPY . .
```
- This line copies all the files and directories from the local directory to the `/app` directory inside the Docker image. It includes the application source code, configuration files, and any other necessary files.


> You must be wondering why copied the package.json file first and then copied all the files. So what happens is this is called layer caching, so when docker builds image using a `Dockerfile` and if there is any change in code itself then docker will just copy the changes and not the things which are same like package.json. This increases speed of creating images.

```
# Build the React app
RUN npm run build
```

- This line runs the command `npm run build` inside the Docker image. With the working directory set to `/app`, it executes the build script defined in `package.json`. This typically involves compiling the React app, bundling assets, and generating the production-ready files.

```
CMD [ "npm", "start" ]
```

- This line specifies the default command to run when the Docker container is started. It uses the `CMD` instruction to execute the command `npm start` inside the Docker image. This typically starts the application server or runs any necessary startup tasks.

So basically your `Dockerfile` will look like:

```
# Base image
FROM node:14-alpine

# Set working directory
WORKDIR /app

# Copy package.json and package-lock.json
COPY package*.json ./

# Install dependencies
RUN npm install

# Copy app files
COPY . .

# Build the React app
RUN npm run build

# Start the app
CMD [ "npm", "start" ]
```

- Save the `Dockerfile`.

## Set up GitHub repository and actions
- Create a new GitHub repository for your project.
- Initialize a git repository in your project folder and commit your files:

```
git init
git add .
git commit -m "Initial commit"
```

- On GitHub, go to your repository and connect it to your local repository:

```
git remote add origin <repository-url>
git push -u origin main
```

- Create a new directory called `.github/workflows` in the root of your project.
- Inside the `.github/workflows` directory, create a new YAML file (e.g., `deploy.yml`).

```
name: Deploy to AWS Elastic Beanstalk
```
This is the name of the workflow. It could be any name you choose.

```
on:
  push:
    branches:
      - main
```
This specifies the event that triggers the workflow. In this case, the workflow is triggered whenever there is a push event to the main branch.

```
jobs:
  deploy:
    runs-on: ubuntu-latest
```
This declares a job named "deploy" that runs on the latest version of Ubuntu operating system.

```
steps:
  - name: Checkout code
    uses: actions/checkout@v2
```
This checks out the source code from the repository.

```
  - name: Set up Node.js
    uses: actions/setup-node@v2
    with:
      node-version: 16
```
This sets up the Node.js environment with version 16.

```
  - name: Install dependencies
    run: npm install
```
This installs the project dependencies using npm.

```
  - name: Build Docker image
    run: docker build -t my-app:latest .
```

This builds a Docker image with the name "my-app" and the "latest" tag.

```
  - name: Log in to AWS Elastic Beanstalk
    uses: aws-actions/amazon-ecr-login@v1
```

This logs in to AWS Elastic Beanstalk using the AWS CLI.

```
  - name: Deploy to AWS Elastic Beanstalk
    run: |
      aws configure set region <YOUR_AWS_REGION>
      aws configure set aws_access_key_id <YOUR_AWS_ACCESS_KEY_ID>
      aws configure set aws_secret_access_key <YOUR_AWS_SECRET_ACCESS_KEY>
      aws elasticbeanstalk create-application-version --application-name my-app --version-label ${{ github.sha }} --source-bundle S3Bucket=elasticbeanstalk-us-east-1-1234567890,S3Key=$GITHUB_SHA.zip
      aws elasticbeanstalk update-environment --environment-name <YOUR_EB_ENVIRONMENT_NAME> --version-label ${{ github.sha }}
```

This deploys the application to AWS Elastic Beanstalk by configuring the AWS CLI with the AWS region, access key ID, and secret access key. It then creates a new application version using the source code from the repository and updates the environment to use the new version.

> Note: The placeholders `<YOUR_AWS_REGION>`, `<YOUR_AWS_ACCESS_KEY_ID>`, `<YOUR_AWS_SECRET_ACCESS_KEY>`, and `<YOUR_EB_ENVIRONMENT_NAME>` should be replaced with the actual values specific to your AWS environment.


You can see the `deploy.yml` file will look like this:

```
name: Deploy to AWS Elastic Beanstalk

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout code
        uses: actions/checkout@v2

      - name: Set up Node.js
        uses: actions/setup-node@v2
        with:
          node-version: 16

      - name: Install dependencies
        run: npm install

      - name: Build Docker image
        run: docker build -t my-app:latest .

      - name: Log in to AWS Elastic Beanstalk
        uses: aws-actions/amazon-ecr-login@v1

      - name: Deploy to AWS Elastic Beanstalk
        run: |
          aws configure set region <YOUR_AWS_REGION>
          aws configure set aws_access_key_id <YOUR_AWS_ACCESS_KEY_ID>
          aws configure set aws_secret_access_key <YOUR_AWS_SECRET_ACCESS_KEY>
          aws elasticbeanstalk create-application-version --application-name my-app --version-label ${{ github.sha }} --source-bundle S3Bucket=elasticbeanstalk-us-east-1-1234567890,S3Key=$GITHUB_SHA.zip
          aws elasticbeanstalk update-environment --environment-name <YOUR_EB_ENVIRONMENT_NAME> --version-label ${{ github.sha }}
```

> Note: Replace `<YOUR_AWS_REGION>`, `<YOUR_AWS_ACCESS_KEY_ID>`, `<YOUR_AWS_SECRET_ACCESS_KEY>`, and `<YOUR_EB_ENVIRONMENT_NAME>` with your own values.

## Create and configure the AWS Elastic Beanstalk environment
- Go to the AWS Management Console and navigate to Elastic Beanstalk.
- Create a new environment and follow the steps to configure it with the appropriate settings.
- Make a note of the `<YOUR_EB_ENVIRONMENT_NAME>` you used in the previous step.

## Commit and push changes
- Stage and commit the changes to your repository:

```
git add .
git commit -m "Added Dockerfile and GitHub Actions workflow"
```

- Push the changes to your GitHub repository:

```
git push origin main
```

## Trigger the deployment workflow
- GitHub Actions detects the push event on the main branch and starts the deployment workflow automatically.
- Monitor the workflow in the "Actions" tab of your GitHub repository.
- Once completed, your React app with the Node.js backend should be deployed to AWS Elastic Beanstalk.

Congratulations! You have now created a basic React app, connected it with a Node.js backend, dockerized the application, integrated GitHub Actions, and deployed it to AWS Elastic Beanstalk. 🎉


# Conclusion
In today's blog, we explored a practical example of deploying a React and Node.js application using the CI/CD pipeline offered by GitHub Actions and deploying the app on AWS Elastic Beanstalk. By following the step-by-step guide, you were able to leverage the power of automation and streamline your deployment process. This blog series on GitHub Actions concludes here, but if you want to delve further into the topic or have any specific requests for future content on GitHub Actions, please leave a comment and let me know. I'm always eager to bring you more valuable insights and tutorials. 

Happy deploying! 😊🚀