React Proxied via Lambda

Patrick O'Connor
Geek Culture
Published in
6 min readApr 26, 2021

Overview

React, as with many other frameworks such as Angular and Vue, seem to be paving the way for the future when it comes to web development. Gone are the days of static HTML with vanilla CSS, replaced now with the entire website loaded, on the very first load of the site. This enhances the user experience with rapid transitions between pages, importing custom libraries that cover a vast range of front-end headaches, and introducing complex logic. React is exciting, allowing easier innovation for developers experienced in the framework.

Now let’s say your curiosity is peaked, you’ve gone off and made a local site that you’re now ready to publish, well then what are the next steps to do so. Specifically focusing on React, the framework will create an optimized production build of the app, typically with the call, yarn build. You can then deploy this across a variety of web hosting services, such as Github pages, Netlify and S3, just to name a few.

So what’s the issue you ask. Why proxy it through a Lambda? Well you see, while S3 is a fantastic service, it isn’t a solution for everyone. There could be various reasons preventing its use, e.g. denial of hosting content on the S3, as this requires the bucket to be public and CloudFront may not be allowed, which calls for an alternate solution.

So what’s the solution?

In this article I’m going to show you a method of hosting a React Single Page Application through the AWS Lambda service. We will be using the three resources of S3, Lambda and an Application Load Balancer (ALB), to serve our built React site on an S3 through to the user.

Basic Architectural Diagram

By end of the workshop our architecture will be that of what’s demonstrated above. So with out further ado, let’s get started!

Step 1: create-react-app

The first step of the workshop is to create a react app and test it locally to ensure all is in check before deploying it. Lucky for us there exists a react generator provided by npx. Make sure you cd into your working directory before executing the following command:

npx create-react-app <REPLACE WITH PROJECT NAME>

*Feel free to give your project any name, as long as it’s all lowercase.

After a couple of minutes the project should now be available at the name you provide. You can run that app locally with.

cd <REPLACE WITH PROJECT NAME>
yarn start

This should launch a local server on your port 3000 (http://localhost:3000/). And there you go, you’ve got a running React app.

Step 2: Configuring AWS architecture/environment

S3

You’ve now tested your website locally so let’s now make it live. We will do this by putting it on an S3 bucket. First we need to make the bucket, so let’s do that from the cli.

aws s3 mb s3://react-serverless-spa-site-<YOUR NAME HERE>
  • Note: replace <YOUR NAME HERE> with your name.
  • You will also need the aws cli installed and configured correctly to your aws account.

Now that we’ve set up our bucket, let’s build and push to it. Run the following command:

yarn build && aws s3 sync build/ s3://react-serverless-spa-site-<YOUR NAME HERE> --delete

Note: Remembering to replace <YOUR NAME HERE> with your name.

Once the terminal concludes its operations, you should now be able to find your React build package uploaded to the bucket you just created.

Lambda

Next we will setup our backend on Lambda. Our aim will be to create a function, push code to this function which will point and serve the content of our S3 bucket. Let’s jump onto the AWS console for this.

On the AWS condole navigate to Lambda > Create Function.

Create Lambda

Configure the project to run Node.js with a function name of your choice.

Once the function has been created we can inject our code. Copy the code below and paste it in the newly created function, into index.js.

*Make sure you add your bucket name on line 4.

const AWS = require('aws-sdk');const s3 = new AWS.S3();
const bucketName = "<REPLACE WITH YOUR BUCKET NAME>"
exports.handler = async (event) => {
try {
let params;
if (event.path === '/') {
params = {
Bucket: bucketName,
Key: 'index.html'
}
} else {
params = {
Bucket: bucketName,
Key: event.path.substring(1)
}
}
let data = await s3.getObject(params).promise()
return {
statusCode: 200,
headers: {'Content-Type': data.ContentType},
body: data.Body.toString()
};
} catch (err) {
if (err.code === 'NoSuchKey') {
let param;
param = {
Bucket: bucketName,
Key: 'index.html'
}
let data = await s3.getObject(param).promise()
return {
statusCode: 200,
headers: {'Content-Type': data.ContentType},
body: data.Body.toString()
};
}
}
};

Make sure to Deploy your function once you’ve pasted the code.

Awesome, you now have a backend set up. The last step to make our Lambda fully functional is to give our Lambda read permission to the S3 service. To do this we need to find the execution role for the Lambda.

You can find this under the Permissions tab in your Lambda function.

Click on the role name which will redirect you to the IAM service.

You will see the only attached policy should be a Lambda Basic Execution. Clicking the blue Attach policies button you will have the choice to add many of AWS’s managed policies to your execution role.

Search for S3 in the filter and select AmazonS3FullAccess. Attach policy.

You’ve now setup your backend, time to expose it.

Application Load Balancer

The final step is to expose your backend to the client over http. For this we will be using an application load balancer, however you may use API Gateway for this step also.

To create a load balancer, we need to navigate to the EC2 service in our AWS console. On the left side menu, near the bottom is where the load balancers sit.

After selecting Create Load Balancer you should choose the Application Load Balancer option and fill out the interface to match the below image. Name the Load Balancer however you feel but make sure it’s internet-facing, Listeners are HTTP, and add it to your default VPC with all subnets chosen.

For step 3 select create a new security group unless you already have an existing security group which allows inbound on port 80, you can alternatively use that group instead.

For configuring routes we will create a new target group and specify that the target will be a Lambda.

On the target registration page, we will be asked as to what Lambda we would like to point our ALB to. Here we will choose the function we created earlier from the dropdown menu.

On the review page make sure everything's in check, if so then Create.

Done

There you have it. If you now go to your Load Balancer and paste the DNS into your browser, your site should now render.

Your load balancer might take some time to provision, so just give it a while and try the DNS again.

Acknowledgment

Credit to Paul Kukiel who came up with the foundational units for this method. His work on this method demonstrated it would be possible to proxy render content through a Lambda, which is what laid the foundation for me to build on top with a React app.

--

--

Patrick O'Connor
Geek Culture

WorldWide Prototyping Engineer at Amazon Web Services (AWS). My opinions are my own.