How to deploy a React app on AWS using the AWS CDK

Andrew Bestbier
May 27 · 12 min read

Implemented with TypeScript, HTTPS, a CDN, CI/CD with Buddy and your own domain name.

What we will be building:

Our React application will be:

  • Hosted on AWS S3
  • Distributed around the world with AWS CloudFront’s CDN,
  • Have a domain name through AWS Route 53
  • Have HTTPS enabled through certificates provided by AWS Certificate Manager
  • Built fully with Infrastructure as code through the use of AWS CDK where we will build the infrastructure with TypeScript
  • Deployed continuously with Buddy. Both the React client app and Infrastructure will have their own CI/CD pipelines

Here is a deployment of the app we will be building: (Notice the custom URL and HTTPS).

An architecture diagram of what we will be building looks as follows:


Infrastructure as code with AWS CDK:

Deploying a React app on AWS S3 is not a new topic on Medium. I personally have written the equivalent of this blog previously here where I outline how to do this entirely through the AWS console UI. However, in this article we will be building our infrastructure with code which can be defined as follows: “Infrastructure as code is a methodology of managing one’s infrastructure using configuration specifications expressed with code”.

In essence, rather than building one’s infrastructure with AWS’s UI, you can write a specification that defines the infrastructure you require. This specification is then provided to AWS CloudFormation which then spins up the underlying services for you.

In this article we will be using the AWS CDK which allows us to write these specifications with TypeScript as well as many other languages. As an example, to create a bucket on AWS you can simply write the following in TypeScript:

If you run the respective CDK commands (which I’ll provide later), AWS will spin up an S3 bucket for you. By building your infrastructure as code, you can easily replicate infrastructure elsewhere, apply logic to your specifications just like you do with normal code (boolean logic, loops etc) and share infrastructure in modules. Writing these specifications with TypeScript is a joy as you can simply use the same language that you use to build your frontend/backend to build your infrastructure as well.

Project setup:

Open your terminal and navigate to the directory you wish to create your project in. Then type the following commands to create the base directory, a git repository and the directory that will hold your infrastructure code:

# Create the base directory
mkdir my-first-cdk-project && cd my-first-cdk-project
# Initiate a git repository
git init
# Create the directory for infrastructure
mkdir infrastructure

Next install the AWS CDK with the instructions found here. Navigate to your infrastructure directory and scaffold a CDK project:

cd infrastructure
cdk init app --language typescript

Next we want to also initialise a React application which we will do with Create React app:

cd ..
npx create-react-app client --template typescript

You should have the following directory structure:

S3 Bucket setup with AWS CDK:

Our first piece of infrastructure will be an AWS S3 bucket that will hold our built React application. First we need to install the @aws-cdk/aws-s3 package so that we can create S3 buckets:

cd infrastructure
npm install @aws-cdk/aws-s3

Open infrastructure/lib/infrastructure-stack.ts in your favourite editor. This file will modified throughout this guide to modify our infrastructure stack. Add the following code to the InfrastructureStack constructor:

If you examine the AWS CDK documentation here you’ll find that all I did above was create a simple bucket with the name “andrew-bestbier-cdk-blog”. We will configure this in more depth later but for now it can be deployed. Deploy your infrastructure with the following commands:

cdk bootstrap # Required only the first time you ever deploy
npm run build # Required every time to compile TypeScript
cdk deploy --require-approval never

AWS CDK will then deploy your infrastructure using AWS CloudFormation under the hood. Head over to AWS CloudFormation to view your newly deployed stack:

If you click on InfrastructureStack and then click the Resources tab, you will see the AWS resources that have been created. Click on your S3 bucket:

You should see your brand new empty S3 bucket:

Uploading your React app to S3:

Next we can manually upload the built React app to your S3 bucket (we will automate this later). Run the following commands to do so:

cd .. && cd client
npm run build
aws s3 cp build s3://<your bucket name>/ --recursive

If you view your bucket now, it should have the your files:

Website hosting with S3:

Now that we have a good foundation in place, we can configure our bucket for static website hosting. Add the following infrastructure code and install the @aws-cdk/aws-iam package:

Note how we are doing a few things here:

  1. Specify the index document for our website
  2. Allow public access to our bucket
  3. Create a bucket policy allowing anyone to access the objects in our bucket
  4. Add the policy to our bucket

Next deploy your infrastructure with the commands provided earlier. If you open the Properties tab within your bucket and then click on the Static website hosting box, you should see the endpoint of your website:

If all goes well, you should see your hosted React app:

CI/CD setup:

The process we have used so far is a bit clunky in that we have run commands locally to sync our built React application up to S3 as well as commands that build and deploy our infrastructure. In this section we will be refining both of these processes by:

  • Hosting our code in Github
  • Creating a CI/CD pipeline with Buddy that will deploy the React application
  • Creating another CI/CD pipeline with Buddy that will deploy infrastructure changes with the AWS CDK

Hosting code in Github:

Head over to Github, click the + icon and select New repository:

Next name your repository (I named mine cdk-react) and click Create repository:

You will then be shown commands to push our existing local repository with both the client and infrastructure code up to Github. Mine looks as follows:

git remote add origin
git push -u origin master

Commit your code, run these commands and your code should appear in Github:

Creating a React CI/CD pipeline:

Now that our code is in Github, login to Buddy where you should see the following dashboard if this is your first time using the service:

Click Create new project, connect Buddy with Github and link the repository you created earlier:

Buddy should then intelligently detect that your repository contains a React application. Click Add a new pipeline:

Name your pipeline “Build and deploy React application”, trigger it On push and then click Add a new pipeline:

Now that your pipeline has been created, we can add our first action. Search for the Node.js action and click the action:

Next we are prompted to add some commands we wish to run. Add the following commands to install your dependencies and build your application and then click Add this action:

cd client
npm install
npm build

Now that our application is built, we need to add another action to upload our application files to S3. Search for S3 and click on the action:

A modal will appear prompting you to add an AWS integration.

This requires that you use AWS IAM to create a programmatic user with appropriate permissions. If you require a guide on how to do this, I have written one here for another blog. Ensure your user has AmazonS3FullAccess , AWSCloudFormationFullAccess, AmazonRoute53FullAccess and CloudFrontFullAccess as this user will need these permissions later on this is article:

Once you have added the credentials of your AWS IAM user the modal will disappear, leaving you to configure the S3 action. Provide the source path as client/build from the Pipeline Filesystem, select your S3 bucket you wish to upload to and click Add this action.

Your pipeline should look like this:

Test your React pipeline out by committing a change to Github. Buddy will automatically run your pipeline, build your application and upload the result to your bucket.

Creating a CI/CD pipeline for AWS CDK:

Now that CI/CD is in place, we can do the same for your AWS CDK infrastructure code. Go to your Buddy project and click Add a new pipeline:

Name your pipeline “Build and deploy AWS infrastructure”, trigger On push and click Add a new pipeline:

Now that your pipeline has been created, we can add our first action. Search for the Node.js action and click the action:

Add the following commands to install your dependencies and build your application and then click Add this action:

cd infrastructure
npm install

Now that our application is built, we need to add another action to deploy our infrastructure. Search for the AWS CLI action and click the action:

We use the AWS CLI option as we can specify the AWS account we wish to use. Add the following commands and select your AWS account that you used for the React pipeline:

cd infrastructure
cdk deploy --require-approval never

Before adding the action we also need to edit the Environment of the job as the AWS CLI action does not have AWS CDK installed by default. Click the Environment tab and add the following commands to install npm and, with npm, install the aws-cdk before clicking Add this action:

apt-get update && apt-get -y install nodejs npm
npm install -g aws-cdk npx

Your final pipeline should look as follows:

CloudFront CDN setup:

At this point we have a very strong foundation to build upon. Our CI/CD pipelines continuously deploy our React app and infrastructure which is also built with code. In this section we will setup a CDN with AWS CloudFront so that the React application can be served from edge locations all around the world rather than directly from the S3 bucket itself:

To make this change, we will need to make a few modifications to our infrastructure but first, install the AWS CDK CloudFront library:

cd infrastructure 
npm install @aws-cdk/aws-cloudfront

Before we spin up our CDN with CloudFront we need to understand the concept of Origin Access Identity (OAI). An OAI restricts access to an S3 bucket and its content to only CloudFront and operations it performs. So we will now create an OAI, grant it read access to the bucket and remove the bucket policy and code allowing people to access the bucket directly:

If you try access your S3 website URL your access will be denied:

Next we can create a CloudFront distribution and specify both the CDN bucket source as well as OAI:

Our CloudFront CDN will take around 5 minutes to spin up. Your distribution should then be visible in CloudFront:

If you click on the Domain name provided, you should see your application:

Invalidating CDN cache:

If you push a change to the React application at this point, the build will be uploaded to your S3 bucket through the pipeline. However, if you view the URL provided by CloudFront, you’ll notice that the application displayed is now outdated. To fix this we will need to add a step to our React CI/CD pipeline to invalidate the cache.

Search for the CloudFront action in Buddy that will invalidate the cache:

Next select your Distribution and click Add this action:

Your pipeline should now look as follows:

Adding a custom domain name and HTTPS:

We can now polish our application by buying a domain name and ensuring that HTTPs is used. For this section we will do the following:

  • Purchase a domain name through AWS Route 53
  • Create a certificate through AWS Certificate Manager
  • Assign our certificate to our CloudFront distribution

Purchasing a domain name through Route 53:

Head over to the Route 53 service and search for the domain name you wish to purchase (I chose and click Continue:

You will then be prompted for some personal details. Enter these and click Continue:

It will take some time but eventually your domain will be registered:

Creating a certificate with AWS Certificate Manager:

First install the Certificate Manager and Route53 dependencies:

npm install @aws-cdk/aws-certificatemanager @aws-cdk/aws-route53 @aws-cdk/aws-route53-targets

Next add the following code:

Note how we are doing a few things here:

  1. Creating a reference to our Hosted Zone by looking it up in Route 53
  2. Creating a DNS validated certificate that will be automatically validated against our hosted zone in Route 53. Note how we need to create the certificate in us-east-1 as it will be used by CloudFront which is a global service

If you deploy your infrastructure and open AWS Certificate Manager in N. Virginia (us-east-1) you will see your certificate:

AWS would have also added a CNAME record to the DNS configuration for your domain in Route 53. Our final step is to add the certificate to our CloudFront CDN and create a new A Record in Route 53 to point to our CDN:

Note how we are doing a few things here:

  1. Specify the certificate our CloudFront distribution should use
  2. Setting the security policy
  3. Setting the SSL method
  4. Creating a new A Record in Route 53 to point to our CloudFront distribution

If you now try access your domain, you will see your React application:


If you have made it this far, congratulations! That was a long article and I really hope you learned a lot. If you have have any questions feel free to ask them in the comments below. Also, if you spot any errors or have suggestions please let me know!

AVM Consulting Blog

AVM Consulting — Clear strategy for your cloud

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store