Static Site Deployment with AWS S3

Hands-On Lab

 

Photo of Fernando Medina Corey

Fernando Medina Corey

Training Architect

Length

01:00:00

Difficulty

Intermediate

In this lab we’ll be deploying our static site to make use of our API Gateway endpoints. We will demonstrate how you can setup a static site on AWS S3 as well as how you will need to configure your S3 bucket to work effectively with API Gateway APIs.

What are Hands-On Labs?

Hands-On Labs are scenario-based learning environments where learners can practice without consequences. Don't compromise a system or waste money on expensive downloads. Practice real-world skills without the real-world risk, no assembly required.

Static Site Testing and Deployment

In this lab, we will be deploying our own full-stack, serverless static site with the API gateways we created. The site is supported by AWS. On this site, we will see how all of our methods, Lambda functions, and our DynamoDB tables work together.

Setup

Before we start, there are a few things we need to do first.

Head to github to get the information needed for this course. Use the download function to get a zip file of the documents, as we will be editing some of the base code in your preferred text editor. For the corresponding video to this guide, the Lab creator uses Sublime Text.

Open the index.html file both in your preferred browser and in your preferred text editor. We will be making updates to the code throughout the lab.

Sign into the AWS Console with the provided credentials. Once signed in, go to API Gateway. Choose Prometheon. Select Stages, Dev, and then copy the Invoke URL that appears. This URL will change with each lab, so if you have to restart for any reason, you will need to get the new URL each time. Save this to your clipboard or a note for later use.

Next, we need to update the formlogic.js file with the Invoke URL we copied. Go to your text editor and open the formlogic.js file. Find the line that begins var API_ENDPOINT = and replace everything in the single quotation marks up to the /prometheon with the Invoke URL. It is important to leave the /prometheon as the methods we created for this site are located in that resource. Once we've changed the code, save the file.

Open the index.html file in your preferred browser. The Prometheon Music Manage page appears. Right click on the page and select Inspect. Go to the Console tab and search for API_ENDPOINT. The URL we placed appears. If not, make sure that you saved the update in the formlogic.js file and then refresh the URL page.

With all this set up, we can now begin the lab!

Testing Our API Locally

For this portion of the lab, we suggest having two windows open; one for your URL page and one for your editor. You can also flip between the two if that is easier for you.

Testing the list Function

Before we do our test, let's go over the code just a little. The following code is used in all of the functions with minor changes. For example, the 'listbutton' will change to 'deletebutton' and so on for each button type.

document.getElementById('listButton').addEventListener('click', function (event) {

All of the buttons have the //Prevent section we have set up. Instead of having the page refresh whenever performing a function, the page instead will clearNotifications. This code is used in each of our functions.

    // Prevent the page reloading and clear exisiting notifications
    event.preventDefault()
    clearNotifications()
    // Prepare the appropriate HTTP request to the API with fetch
    // list uses the base /prometheon resource path and doesn't require
    // a payload or query string paramaters

Our fetch reflects the GET method that we created in the previous lab and how it connects to the cors mode.

    fetch(API_ENDPOINT, {
        method: 'GET',
        mode: 'cors'
    })

All of the functions hold the following code, designating a success or failure when running the function.

    .then((resp) => resp.json()) 
    .then(function(data) {
        console.log(data)
        successDiv.textContent = 'Success! Check the fetched items below';
        resultsDiv.textContent = JSON.stringify(data);
    })
    .catch(function(err) {
        errorDiv.textContent = 'Yikes! There was an error:n' + err.toString();
        console.log(err)
    });
});

With our setup complete, it is time to test our Promethon Music Manager page. To get started, select the list button.

Whoops, we already have an error. Right-click and select Inspect, then choose the Console tab. It looks like our information failed to load because we have no CORS header present in our request source. But wait, we didn't have to do anything with CORS headers before, right? That is true, but the program Postman that we used does not require CORS headers while web browsers do as a safety precaution. This security issue causes our listing attempt to fail. To fix it, we need to provide all of our lambda functions with a CORS header that will let our browser know that it is okay to pull this information.

Head back to the AWS console and then to the Lambda section. Select the list function, as this is what we are currently working with. In the Function Code section, scroll to the end of the code block:

    // create a response
    const response = {
      statusCode: 200,
      body: JSON.stringify(result),
    };
    callback(null, response);
  });
};

In here we need to add our missing header information. Copy the missing header name from the warning we got after inspecting the Promethon Music Manager page, Access-Control-Allow-Origin. Now, under the statusCode line, we will enter our heading code:

    // create a response
    const response = {
      statusCode: 200,
      headers: {'Access-Control-Allow-Origin': '*'},
      body: JSON.stringify(result),
    };
    callback(null, response);
  });
};

The * that we included tells the response code that it can send a response to any domain that requests our API endpoint. Note that this is a very permissive header, and when creating your own pages, you may want to set stricter domain allowances.

With that information entered, select Save and then head back to the Prometheon Music Method page. Click on list. This time, we get a success message along with a list of ten items at the bottom of the page.

Update the Functions

Just like we did with the list function, we need to go in and update all of the other functions as well.

Go back to Lambda and then select Functions. For the create, delete, get, and update functions, add the missing header code into their Function code in the //create a response section:

    // create a response
    const response = {
      statusCode: 200,
      headers: {'Access-Control-Allow-Origin': '*'},
      body: JSON.stringify(result),
    };
    callback(null, response);
  });
};

Test the create Function

With all of our functions updated, we can go through and test the rest of our functions. For these steps, we will seemingly be going out of order, but it is for a reason. We are going to create an item, get that item, update it, and then delete it.

Note that our function code is taking information from the "application/json" files we created earlier.

    fetch(API_ENDPOINT, {
        headers:{
            "Content-type": "application/json"
        },
        method: 'POST',

As this is a create function, we also have the body that will be populated with the information we input.

        body: JSON.stringify({
            Artist: ArtistValue(),
            SongTitle: SongTitleValue(),
            AlbumTitle: AlbumTitleValue(),
            CriticRating: CriticRatingValue(),
            Genre: GenreValue(),
            Price: PriceValue()
        }),
        mode: 'cors'

To test our create function, enter the word test into all fields. All sections must be filled. If they are not, then nothing will be returned, and we will get an error. With everything filled out, and our header in place, the create button will perform a successful creation and display the new item at the bottom of the screen.

Let's double check that the success is really a success. To do so, go back into AWS and head to DynamoDB. Select Tables, head to PrometheonMusic, the items tab, and then perform a Query for test in the Partition key section.

The item we just created appears.

Test the update Function

This is one of our items where our API_ENDPOINT will look a little different. The reason? Our fetch code adds /id to the end of our API_ENDPOINT when used because that is the resource that holds our put method which in turn runs the update function. Again, the body information is present since all of those are needed when creating an update.

    fetch(API_ENDPOINT+'/id', {
        headers:{
            "Content-type": "application/json"
        },
        method: 'PUT',
        body: JSON.stringify({
            'Artist': ArtistValue(),
            'SongTitle': SongTitleValue(),
            'AlbumTitle': AlbumTitleValue(),
            'CriticRating': CriticRatingValue(),
            'Genre': GenreValue(),
            'Price': PriceValue()
        }),
        mode: 'cors'

Keep everything set as test except for the CriticRating, change that to 9.9. Click the update button. We get back a message saying "Item Updated".

To double check, go back into AWS and head to DynamoDB. Select Tables, head to PrometheonMusic, the items tab, and then perform a Query for test in the Partition key section. When it appears this time, the CriticRating section is updated.

Test the get Function

With get, the code section changes to the/id section and pulls in the method GET along with the base information.

    // Prepare the appropriate HTTP request to the API with fetch
    // get uses the /prometheon/id resource path and query string paramaters
    // The final result is a GET request to something like:
    // https://example1a2s3d.execute-api.us-east-1.amazonaws.com/dev/prometheon/id?Artist=Rihanna&SongTitle=Work
    fetch(API_ENDPOINT+'/id?Artist='+ArtistValue()+'&SongTitle='+SongTitleValue(), {
        headers:{
            "Content-type": "application/json"
        },
        method: 'GET',
        mode: 'cors'
    })

Keep everything in the fields the same as it was for update and click on get. At the bottom of the page, the item we updated appears.

Test the delete Function

The following delete code changes mostly in the body section. This time, only the Artist and SongTitle is needed instead of all of the items.

    // Prepare the appropriate HTTP request to the API with fetch
    // Delete used the /prometheon/id resource path 
    // It also requires a JSON body payload
    fetch(API_ENDPOINT+'/id', {
        headers:{
            "Content-type": "application/json"
        },
        method: 'DELETE',
        body: JSON.stringify({
            'Artist': ArtistValue(),
            'SongTitle': SongTitleValue()
        }),
        mode: 'cors'
    })

Again, keep the fields already present and select delete.

In AWS, perform another query for the item. This time nothing will appear.

Note that there is a chance you will receive a failure from the site, but if you perform a get or test on AWS, it will show it was actually successful.

Deploy and Test the Site

With a successful test of our site locally, it is time to deploy!

Go to AWS and find the S3 console. We need to create a new S3 bucket. Select Create bucket. You need to create a unique name for the bucket. Remember, if for any reason you need to restart the lab, or are doing it again, then the bucket name you previously used must be modified. In our lab video, we use purple-cat as our example bucket. You will not be able to use purple-cat since it has already been taken.

Once you've named it, click the Next button.

Leave the defaults on the second page and select Next.

On the Set permissions page, change the Manage Public Permissions section to Grant public read access to this bucket. This is not recommended outside of the lab as it will allow anyone to work with your bucket. Once set, select Next.

On the review screen, select Create Bucket.

Select your created bucket. When open, select Upload to add all the files you downloaded at the beginning of the lab. Drop them into the bucket and then select Next. Set the permissions to Grant public read access to this object(s) again and then click Next for both this page and for Set properties. On the Review page, select Upload.

Refresh the screen and all four files appear.

Select the Properties tab.

On this page, select Static Website Hosting. In the parameters, select Use this bucket to host a website, set the Index document to index.html and the Error Document to error.html. We don't need to do anything else, so select Save. Note that the fields on this page will already seem to have those files in them. These are just place holders that the lab files are named after; we actually have to type in the file names.

Select Static Website Hosting and click on the link it provides.

Our Prometheon Music Manager appears.

Perform the following just as we did on our local server. Enter test in each section. Perform a create, then a get, then update the CriticRating information. Perform another get to check the changes, then delete it, and get one last time to make sure it has deleted.

Review

Congratulations! You have deployed your own full-stack, serverless application on AWS! Feel free to keep working within the provided lab instance to practice with all of the pieces of your application.