Today’s DevOps tools allow for much more flexible deployments and application workflows. One interesting pattern I’ve seen develop is a Serverless React application. In this post, I’ll go through one of the quickest and easiest ways to get started with this style of application.

If you would like to skip the article and access the source code right away, please visit https://github.com/chroto/devops-gorilla-react-serverless/tree/master

Before You Begin#

Before continuing, these things must already be set up. If you’re not familiar with these topics, I supplied some links for more information.

  • AWS Account and a Credentials File
  • awscli
  • GitHub Account (If you’re new to git or GitHub, please start by reading GitHub’s guide )
  • Homebrew (if your Development machine is OSX)

Make sure you have all these things set up before continuing.

Bootstrap the Application#

First, you will want to start a codebase for developing your React application. The best way to do this is to install the create-react-app tool. It is the official, Facebook-supported tool for templating a React application.

Also, make sure you install npm >= 5.2.x on your development machine. This will ensure we get the npx tool that comes with NPM versions 5.2 and above.

In OSX,

> brew install nodejs
> npm install -g npm>=5.2

In CentOS,

> yum install nodejs
> npm install -g npm>=5.2

Make sure you have the right versions. Any version of npm > 5.2 will do:

> npm --version
5.8.0

Now, let’s kickstart our project

> mkdir -p my-serverless-react-app
> cd my-serverless-react-app
> npx create-react-app app

You’ll now have a bootstrapped React application underneath the app directory. To make sure everything went well let’s run some tests.

> cd app
> npm test

If your tests passed, we can try running the application.

By running npm start, a web server will start and your browser should automatically navigate to the running application. If it doesn’t, you can visit it by navigating to http://localhost:3000/

Install URLs with React-Router#

At this point, we have a functioning application, but it can only display one page. Let’s setup some more URLs using React-Router.

  1. Install react-router-dom by running npm i react-router-dom --save-dev
  2. Open the file src/App.js in your editor of choice.
  3. Replace the code with the Basic Example from the React-Router documentation
  4. Save and close the src/App.js file
  5. Reload the application in your browser. If you are still running the development server, it will auto-reload the page for you.

At this point, you now have the following:

  • React codebase that’s ready for development
  • Auto-Reloading Server to speed up development
  • Testing Framework to validate our code as we develop
  • URL Router for adding more pages and features

This is a good starting point to develop a full React application. To wrap this section up let’s make sure we track our code changes with git.

Source Code Management#

We’ll use GitHub to host our application’s source code.

Initialize the Local Git Repository#

First, convert our directory into a git repository. This will make sure our changes are tracked and we can rollback to earlier versions if we make a mistake.

  1. Make sure your terminal is in the my-serverless-react-app directory
  2. Initialize your git repository by executing git init
  3. Prepare your files to be committed. Run git add app .
  4. Commit your files: git commit -m’Initial Commit'

Create a Remote Repository on GitHub#

After setting up our git repository, let’s set up a place in GitHub to host our code.

  1. Log into your GitHub account at https://github.com/login
  2. In the top right, click the plus sign icon and click “New Repository“
  3. On the next screen, fill in a name for your repository next to your username “my-serverless-react-app“. Leave everything else Blank
  4. Create new Repository on GitHub
  5. Click the Green Create Repository Button button
  6. Follow the set up instructions “…or push an existing repository from the command line”
  7. After pushing, refresh the GitHub page and you should now see your code

AWS Serverless Hosting#

We’ll use a specific configuration of S3 and CloudFront to host our React application. S3 is AWS’ general purpose storage service that we’ll use to upload and store our React application. CloudFront is a Content Distribution Network and is designed to distribute files and web pages all around the globe. CloudFront can also cache our React application and makes it easier for users to reach our application.

I will use Terraform to set up the infrastructure. Optionally, you can set up S3 and CloudFront manually.

Getting Started with Terraform#

Terraform is a tool that takes declarative code that describes an AWS setup and implements it automatically. In this section, I’ll walk through setting up a basic Terraform project to track your Serverless AWS infrastructure.

Manage Terraform Installation with tfenv#

I recommend using the tool tfenv to manage the installation of Terraform.

For OSX,

> brew install tfenv

For CentOS,

> git clone https://github.com/kamatama41/tfenv.git ~/.tfenv
> ln -s ~/.tfenv/bin/* /usr/local/bin

You can test installing tfenv by running tfenv –version .

Initialize the Terraform Project#

We’ll need a few things to get Terraform set up:

  • S3 Bucket to track the Terraform state files
  • A main.tf file with some initial configuration

Bootstrap Files#

In your project directory, run the following:

> mkdir terraform

to create a directory to store our Terraform configuration files.

Open a new file called main.tf and paste in the following:

provider "aws" {
 region = "us-east-1"
}

terraform {
 required_version = "0.11.5"
 backend "s3" {
  key="serverless-react-app/terraform.tfstate"
  region = "us-east-1"
  }
}

This is the minimum configuration we’ll use to initialize the project.

Create an S3 Bucket for Terraform#

At this point, we’re also going to set up an S3 bucket that Terraform will use to store information. The default is to store this locally but it is much safer to store it somewhere remotely.

> aws s3api create-bucket --bucket "terraform-$(python -c 'import uuid; print uuid.uuid1()')"
{
 "Location": "/terraform-108881ea-3616-1108-984f-3035bdcxxxxxx"
}

Save the name of the bucket and we’ll pass this to terraform init when prompted.

Using Terraform: Modules and Outputs#

With Terraform modules, we can quickly define the resources configured for our use case.

To set up the proper S3 and CloudFront configuration open up the main.tf file and add the following module definition to the end of the file:

module "my-app" {
  source  = "chroto/serverless-react/aws"
  version = "1.0.0"
  ## NOTE: The name variable generates the s3 bucket name. 
  ## It is recommended that you change the name variable to something more unique to avoid a name collision 
  ## Remember: S3 bucket names must be globally unique. 
  name = "my-serverless-react" 
}

Whenever a module is added to a Terraform project, terraform init must be run again. Re-run terraform init and take care to use the same S3 bucket from the previous section.

Now, open up a new file and call it outputs.tf . This file will expose specified properties of our AWS environment and tell us a bit about what Terraform created. In the outputs.tf file, paste in the following:

output "s3_bucket" {
 value = "${module.my-app.s3_bucket_id}"
}

output "domain_name" {
 value = "http://${module.my-app.domain_name}/"
}

We’re now ready to generate our AWS resources.

Creating a Plan and Applying#

Using the terraform plan command, we can generate a description of what Terraform will change, create, or delete. This will not actually make any changes to your AWS account.

When you run terraform plan, you should see four resources prepared for creation.

+ module.my-app.aws_s3_bucket_policy.this
+ module.my-app.aws_s3_bucket.this
+ module.my-app.aws_cloudfront_origin_access_identity.this
+ module.my-app.aws_cloudfront_distribution.this

and one data source to be read: <= module.my-app.data.aws_iam_policy_document.s3_policy.

Take a minute to review the plan that was generated. If it looks correct, then it’s time to run the terraform apply command. This is similar to terraform plan except that it will prompt the user to execute the plan in the AWS account.

After typing “yes” to verify, Terraform will begin configuring AWS resources. After it is finished, it will output the domain name for the CloudFront distribution and the S3 bucket. The domain name is where the React application will be hosted. The S3 bucket will store the application files.

At this point in the process, we have all the resources we need to host our React application without managing any servers. All that’s left is to build our application and deploy it to S3.

Launch the Application#

In your terminal, navigate back to the application directory app/ . This is where we will build and deploy our application.

To build a production version, run the command npm run build. This will prepare and compile your React source code into a directory of files that can be run statically. All of these files are located inside the build directory.

This build directory is the entire package that we’ll upload and store in S3.

From the app/ directory, run the following command remember to use the S3 bucket name generated after terraform apply)

> aws s3 cp --recursive build/ s3://[USE S3 BUCKET FROM TERRAFORM OUTPUT]/

After uploading the build directory to S3, you can now visit the CloudFront domain name (also available from the Terraform output). What you should find is a fully browsable version of your React application.

Summary#

After reading this article, I hope you are now able to develop a completely serverless React application using S3 and CloudFront.

Next steps from here would be setting up a CI/CD pipeline to automate the build and deployment steps. Since we are hosting on GitHub, we can use something like GitLab or CodeShip to automate the process when a new commit pushes to the master branch.

For more information, please check out the following links, including the source code for the Terraform module that helps set up the Serverless architecture: