This is the first in a three-part series which will provide an example of a completely serverless architecture offering data-driven sites with a node backend and federated authentication. Let’s jump into the first entry and learn how to host our code in an AWS S3 bucket.
Hosting can run the gamut of complexity, pricing and maintenance. Some of the things we think about when hosting are security, scalability, reliability and complexity.
Historically, an application’s server and client were one and the same and hosted in the same location. When we needed to host a website, we would spin up a server or multiple servers (redundancy is good), and place our code on the server to be delivered to the world in the form of rendered static websites.
In this model, we were required to maintain our servers, add more when needed or remove them when not needed. Security, scalability and reliability are in our hands and complexity is usually higher than needed.
In today’s architecture, we are seeing a greater separation between the client code and server code. With the advent of more robust client frameworks and mobile apps, a web application’s backend is often hosted by itself and available to the different clients that communicate with it. So, logically, a web client is also hosted separately. For the remainder of this post, we will be discussing the hosting of a web client specifically.
Hosting a client separately opens some doors to us because all we are worried about is the code that renders on the user’s device. This basically means that all we need is a public folder to store our code. Enter Amazon Web Service’s S3. AWS S3 offers a web hosting enabled bucket that can serve up static files.
The advantage to this is immediately recognizable in some of the core factors we have to look at when hosting. From a security standpoint, it’s a no-brainer. When using AWS S3, we automatically have access to several security measures like AWS Permissions, bucket access control lists, and cross-origin resource sharing limitation.
For scalability and reliability, S3 storage has this built in. AWS S3 buckets are not limited to a single server or group of servers that we need to maintain. S3 is an inherently scalable container backed by AWS to be highly available. Stack CloudFront on top of that and you now have a highly available site that includes geolocation serving as well.
Now that we have some pretty good reasons to use S3 for hosting, I am going to show you how to get started with a simple site and take advantage of S3’s features. In this first part, the site will be static. In later posts, we will start consuming data to make it dynamic. From here on out, I am going to assume you have an AWS account. Let’s get started.
Preparing The Bucket
If you have never used S3 before, you will find it under Storage & Content Delivery on the main dashboard. Click on S3 to enter the dashboard.
On the S3 dashboard click the Create Bucket button to create a new bucket.
Bucket Name: Choose a name for your bucket. When choosing a name, it must be unique because bucket names are shared across all of AWS.
Region: Choose the region you want your bucket to reside in.
Click Create to create your bucket.
Congratulations, you should now have a new bucket in your bucket list. Click on the magnifying glass next to your bucket name to reveal the properties.
As you can see there are many different bucket configurations we can change. For now, we are concerned with the one that says Static Website Hosting. Click to expand the website options.
Choose the Enable Web Hosting radio button. For now, we only need to fill out one thing on the form, which is the Index Document field. For our project, we will be using index.html. When setting up for production, be sure and add an error page. S3 will treat this as a rewrite. Meaning, when an error occurs, it will keep the same URL in place but serve up the error page.
Take note of the Endpoint. This will be the address of your hosted website. This can be modified with the use of Route 53, but that’s for a different blog post. Click Save and you’re done. The endpoint for this demo can be seen at http://serverless-architecture.s3-website-us-west-2.amazonaws.com.
Preparing My Code
S3 hosting can be used with any type of static hosting. You can use a framework of folders and sub-folders, or just a single html page.
In most of my client-side development, I use a framework called Aurelia, with a module bundler called webpack. Because part of this blog deals with hosting a SPA framework, I have used Aurelia and Webpack here as well. So, to prepare for S3 hosting, I have packed up my entire site in a folder called
A lot of these files may not make sense, but there is one I want to point out. You can see the
index.html page. You’ll remember that, when setting up the bucket, we set our Index Document to be
index.html. So it’s pretty important that it exists. The basic idea is to have your code under a main folder.
Getting My Code to My Bucket
Now that we have our hosting bucket in place, we need to get our code into our bucket. I will show you a couple of ways to do this.
The Easy Way
AWS provides an upload process to copy your files from your local computer to your bucket. This is a fairly simple process, but can be tedious when doing development iterations. To get started, click on your bucket name to go into your bucket. You will now see the contents of your bucket, which is nothing.
Let’s fix that. By clicking on the Upload button, AWS presents you with a dialog that will allow you to upload your files into the bucket. Using the AWS uploader, load the contents of your web directory into the root of the S3 bucket. When you are done, your index.html file, and all supporting files should be present in your bucket.
The Slightly Harder, But Much Better Way
If you are a developer that releases code often, you will soon discover that going to the AWS dashboard to upload your code each time is rather frustrating. If only there were a better way… enter AWS CLI. If you haven’t installed and configured the AWS CLI yet, follow the instructions here. When configuring your AWS CLI it is strongly recommended that you use an IAM user with proper credentials.
Once you have your CLI installed and configured, open up a terminal and enter the following command but modified for your specific information.
aws s3 sync [path to folder] s3://[unique bucket name] --acl "public-read"
Let’s break that down:
- aws: invokes the aws cli.
- s3: invokes the s3 command in the aws cli.
- sync: is the command that tells the cli to sync the local folder with the bucket. Beware, if it isn’t in the local folder, it will be removed from the bucket.
- path to folder: this is the path to your local folder. In my case I keep my production code in a folder called dist. This is a sub folder in the root of my project. So, from the root of my project my path would be ./dist/ .
- unique bucket name: for me this is serverless-architecture. So the entire bucket path will be s3://serverless-architecture.
- –acl “public-read”: this is important. This adds an ACL to the bucket that allows the bucket to be read publically.
Ok, ready? Hit enter. If all has gone well, you should see your files being uploaded to the bucket. Once this process is complete, you can load up your endpoint in a browser and you should see your page. Mine looks as follows:
If you are hosting a standard multi-page site, that’s it! You’re done with the basic settings. If you are interested in configuring for an SPA, read on. If not, skip on down to Routing Rules.
Spicing it up with a SPA
Ok, so you’re an old pro at hosting static sites on AWS S3. Let’s change our scenario a bit. One of the new trends happening is the idea of an SPA (Single Page Application). The idea of an SPA is to have a single page that only reloads specific parts of that page, rather than reloading the entire page upon navigation. Let’s add a second page to our site:
When building an SPA with frameworks such as Aurelia or Angular you will deal with client side routing. This is done by using the hash, so a link might look like
serverless-architecture.s3-website-us-west-2.amazonaws.com/#/page2. When using the hash, the browser knows to use the Index Document so this will work out of the box on AWS.
However, let’s be a bit picky. We don’t want that # mark in all of our URLs. We want the URLs to be clean and simple and memorable. This can be accomplished as well. Through the use of pushstate, the client framework can update the browser with the proper URL as navigation occurs. Ok, let’s turn on pushstate in our SPA.
After that change you can see that the # is now gone from our URL and we can navigate back and forth between pages. Success? Not quite. If I try to refresh page 2, I get something unexpected.
So what’s the issue? By turning on pushstate we have removed the # from the URL. This is all well and good when navigating within the application. However, if we do a refresh, the browser looks for a physical page called
page2 in the root of the site. Because we are using a SPA, this page does not actually exist so we get a 404.
This is an easy fix with S3 hosting. If you remember back to the bucket setup we configured the Index Document? Now we are going to take advantage of the Error Document and how S3 handles that routing.
Ok, so here we have set our Error Document to be the same as the Index Document. Because of the rewrite nature of the error page, if S3 throws a 403, which it will when using client side routing for SPAs, it will rewrite to the index page. The index page will then do its magic and route to page2 because it is still the URL in the browser.
Why a 403 and not a 404? The browser throws a 403 because it cannot find the virtual pages of a SPA and then tries to list the directory. Because we do not have listing available to the public, it throws a 403. Remember, this is only happening when a page is being directly hit through an outside link or refreshed at the browser level. Not necessarily edge case, but rare enough that it works for my situation.
For a bucket based hosting, AWS S3 offers us some very “serverish” functions. One of these functions is the ability to write custom routing rules. For example, let’s say you have a page cleverly named
page3. This page is a marketing page and is well known out in the wild. Sadly, the day has come when you need to retire page3 but you don’t want to lose all of that SEO “link juice” your page3 has built up. Let’s setup a Routing Rule to handle this.
Returning to the S3 dashboard on AWS. Click the magnifying glass to bring up the properties of your bucket. Choose Static Web Hosting and expand Edit Routing Rules under the Enable Website Hosting radio button.
We added the following rules to the text area:
Let’s break that down:
- RoutingRules: is the container for rules
- RoutingRule: is a single rule
- Condition: sets the condition that must be met for the rule to be invoked
- Redirect: is what we want to happen when the condition is met
- ReplaceKeyPrefixWith: is what we want to replace our key with.
In plain speak, this says, “Hey, if you get a request for
page3, redirect it to
Routing rules offer a powerful mechanism for handling routing situations, while still allowing us to keep our hosting simple. For more information and examples of Routing Rules please go here.
One last thing I would like to show you about S3 hosting is the ability to handle domain redirection. Let’s say you have a domain
webecool.com. As the owner of WeBeCool, you have to decide if you want people to reach your domain at
www.webecool.com. For SEO and maintenance reasons, you don’t want both, but you certainly don’t want to miss traffic coming in. In S3 hosting, this can be handled. Let’s walk through our scenario.
First, let’s set up a new bucket called
webecool.com to match our scenario. If possible I like to name my buckets according to the domains they will host. This makes it much easier when addressing this bucket from other services and the AWS CLI. Here is what our new bucket looks like.
Next, we are going to create a new bucket called
www.webecool.com. We are still going to use the Static Website Hosting drop down, but this time we are going to choose the Redirect all requests to another host name radio option.
In the Redirect all requests to field, we are going to enter our desired host name, which in our case is
webecool.com. The hostname we redirect to here is not limited to another bucket. It can be any fully qualified domain name (FQDN). Click Save and you’re done.
So now, if a user goes to www.webecool.com, they will get redirected to webecool.com. Now you only need to maintain one bucket and your SEO guy is happy.
One thing to note, in this scenario, I am using specific domain names to make a point. However, to use specific domain names for S3 buckets, rather than the auto-generated ones, you must configure your DNS properly. The easiest way to do this is by using Route 53.
I hope you have enjoyed this post on hosting with AWS S3. As stated before, this is the first post in a series covering “Building a Serverless Architecture on AWS”. Stay tuned for the next installment, where I’ll discuss using AWS Cognito as a serverless authentication provider.