TL;DR: Atwood's law states that Any application that can be written in JavaScript, will eventually be written in JavaScript. In 2018, eleven years after this law was proposed, JavaScript is now the most popular language in the world. In the first part of this tutorial, we covered database and backend deployments. In the second and final part of this tutorial, I'll show you how to tie everything together by learning to deploy JavaScript Single Page Applications and static websites to different cloud platforms. This article is not about performance.


Application Recap

The Single Page Application we will deploy in this tutorial can be found on GitHub. The backend exists here.

Clone the frontend repository to your local machine. Run npm install and run it with the following command:

npm run dev

JavaScript Meetups app Live App stuck

The app should show a list of public meetups. When a user is logged-in, a list of private meetups should be shown. Unfortunately, neither of them shows on the screen. Why? We are not connected to a live backend.

Open up utils/meetup-api.js. The BASE_URL constant is connected to a local server instance. Let's change the value to a backend URL. In the previous tutorial, we deployed our backend to several cloud platforms. All we need to do right now is choose one.

Let's go with the Heroku backend URL which is, https://meetupservice.herokuapp.com. Replace the value of the BASE_URL constant with https://meetupservice.herokuapp.com.

Now, run the app again.

JavaScript Public Meetups page Live App Working

Note: At this point, the private meetups page are not working because we haven't configured the right variables.

At this point, we are running the Single Page Application on our local machine. Let's make the app publicly accessible by deploying to the cloud.

Netlify

Netlify is a powerful platform that allows you to easily push frontend code and have it deployed to the cloud within few minutes. And you can get started for free. It generates a secure publicly accessible URL after deployment.

It provides features such as:

  • Instant Rollbacks
  • Instant Cache Invalidation
  • Atomic deploys
  • Prerendering for Single Page Applications

and much more.

Every time a change is made and pushed to the GitHub repo, a fresh deploy happens automatically!

Let's go ahead and deploy our app.

  • Create an account on Netlify if you don't have one.
  • Create a new site from Git

    Create site from Git Create site from Git

  • Connect to GitHub Choose GitHub Choose GitHub

    Select Repo Select Repo

  • Choose the branch, set the build command and the publish directory. In our case, the build command is npm run build, the publish directory is dist. Enter build commands Enter build commands

  • Deploy site.

    Site deploy in progress

  • Check out your deployed site. Live Site

    Deployed Site

Let's ensure everything works well. We are using Auth0 for authentication.

Auth0 offers a generous free tier to get started with modern authentication.

Ensure Auth0 is configured properly.

  • You need to have an account with Auth0.
  • Go to your Auth0 Dashboard and click the "create a new application" button.
  • Name your new app and select "Single Page Web Applications".
  • In the Settings for your new Auth0 application, add your URL, in my case https://agitated-davinci-4df0c1.netlify.com/callback to the Allowed Callback URLs.
  • Replace the clientID in the SPA with the client id from your Auth0 dashboard.
  • Replace redirectUri in the SPA with the Allowed callback URL from your Auth0 dashboard.
  • Ensure the Allowed Web Origins, Allowed Origins(CORS) in your Auth0 dashboard is set to your live URL. In my case, https://agitated-davinci-4df0c1.netlify.com.
  • Your audience should be the audience you set when you created the API for the backend.

Very Important Note: Since our app is a single page client-side app, without a proper server configuration, the users will get a 404 error if they access https://agitated-davinci-4df0c1.netlify.com/callback directly in their browser. So, we have to fix that on Netlify with the following steps:

  • Create a _redirects file in the root of the app. Add the code below to the _redirects file:

      /*    /index.html   200
    
  • Modify the build command in your package.json's script section to the following:
"scripts": {
    ...
    "build": "node build/build.js && mv _redirects dist"
  },
  • Now, commit the new changes to GitHub. Once you commit, a deploy process will commence on Netlify. And the _redirects file will be moved to the dist folder on Netlify server. Netlify serves the content of the dist folder as the app, so our SPA routing will work fine!

Test the app again in the browser. https://agitated-davinci-4df0c1.netlify.com

Netlify is really powerful. It provides a lot of features out of the box such as:

  • Hooks for triggering a build
  • Asset optimization
  • Webhooks for deploy notifications
  • Domain Management

Surge

Surge is a static web publishing platform designed especially for frontend developers. It's simple to use. You can publish your application without leaving the command line. Surge's free version offers the following:

  • Unlimited publishing
  • Custom domain
  • Basic SSL

Simply install Surge via your terminal like so:

npm install --global surge

Now, follow these steps to deploy your app:

  • Modify the build command in your package.json's script section to the following:
"scripts": {
    ...
    "build": "node build/build.js && cp dist/index.html dist/200.html"
  },

Surge's way of handling SPA client-side routing is providing a duplicate of your index.html as a 200.html file in the root of the folder that gets deployed. The 200 page helps you re-route all requests to your client-side application, improving the usefulness of your URLs.

  • Run the surge command in the terminal. Fill in your email and password. Also, specify the dist directory and press enter:

    Deploying via Surge

  • Check out the URL it generates and visit your app. In my case, it is http://sable-robin.surge.sh.

Very Important Note: At this point, you will need to add this URL to the Allowed Web Origins, Allowed Origins(CORS), and http://<your-url.sh>/callback to your Allowed Callback URLs in your Auth0 dashboard. Furthermore, you'll need to update your src/auth/Auth.js file. Replace the value of redirectUri with http://<your-url.sh>/callback.

Now, re-run the npm run build command to make a new build. Also, run the surge command but with some parameters like so:

surge dist/ <your-surge-url>

Your app will be updated. Everything should work fine now!

Surge Live App

Google Firebase

Google Firebase is a great platform to host your static websites, progressive web apps and single page applications. Firebase hosting provides fast and secure hosting for your web app. With a single command, you can easily deploy web apps and static content to a global content-delivery network (CDN).

Firebase hosting offers the following:

  • Apps Served over a secure connection
  • Free SSL certificates
  • Fast content delivery
  • One-click rollbacks
  • Free

Install the Firebase CLI on your machine like so:

npm i -g firebase-tools

Go ahead and run the login command:

firebase login

Authenticate with your Google account and give Firebase CLI the permissions it requests by clicking the Allow button.

Initialize a Firebase project in the app directory:

firebase init
  • Choose Hosting as the Firebase CLI feature provided in the interactive widget.
  • Select Create a new project.
  • Head over to https://console.firebase.google.com to create a new project. Run firebase init again in the terminal. Go through the steps and choose the newly created project.
  • Modify the firebase.json file that is currently in your root directory to:
{
  "hosting": {
    "public": "dist",
    "rewrites": [ {
      "source": "**",
      "destination": "/index.html"
    } ],
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ]
  }
}

public is mapped to the dist folder. This ensures that the content of the dist folder is uploaded. The rewrites rule is to enable client-side routing for our app.

Very Important Note: At this point, you will need to add this URL to the Allowed Web Origins, Allowed Origins(CORS), and http://<your-url.firebaseapp.com>/callback to your Allowed Callback URLs in your Auth0 dashboard. Furthermore, you'll need to update your src/auth/Auth.js file. Replace the value of redirectUri with http://<your-url.firebaseapp.com>/callback.

  • Now, run npm run build to rebuild the project.
  • Run firebase deploy again to re-upload the new content.

Your app will be updated. Everything should work fine now!

Firebase Live App

Aerobatic

Aerobatic is a platform for deploying static websites and single page applications. It's not so popular but it is very powerful and provides a blazingly fast performance.

It offers the following:

  • Continuous Delivery
  • Plugins for redirects, password protection, http proxy, custom errors, etc
  • Apps served over a secure connection
  • Free 30-day trial
  • Support for Jekyll, Hugo, Hexo, Yeoman, React, Angular, etc

Unfortunately, as at the time of this writing, Aerobatic doesn't support HTML5 pushState. There are claims that it does support client-side routing but I haven't found a complete example to back up this claim.

Amazon S3

Amazon Web Services as we already know is an incredible cloud platform for hosting your web applications. Amazon S3 is a web service offered by Amazon Web Services (AWS) that provides storage through web services interfaces. You can also host a static website and Single Page Application on S3.

Let's follow the steps below to deploy our Single Page Application:

  • Login to Amazon AWS
  • Head over to S3
  • Create a bucket.

    Create an S3 bucket

  • Go to Properties of the newly created bucket and enable Static Web Hosting. Add index.html to the Index and Error document. Note: Write down your URL endpoint.

    Enable Static Web hosting Enable Static Web hosting

    S3 endpoint Write down the S3 endpoint URL

  • Go to Permissions > Bucket Policy of the newly created bucket and add the policy below:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "AddPerm",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::<bucket-name>/*"
        }
    ]
}

Replace <bucket-name> with the name of your bucket. Let's break down the code above:

  • Principal: This is the who. Using a *, we're saying everyone. Literally, everyone.
  • Action: This is what the principal can do. We're saying everyone can perform a "get."
  • Resource: This is the which. Against which resources (objects) can the principal perform this action?

Enable Static Web Hosting

  • Remember the endpoint URL you wrote down? You'll need it now to configure authentication for our app. At this point, you will need to add this URL to the Allowed Web Origins, Allowed Origins(CORS), and http://<your-url>/callback to your Allowed Callback URLs in your Auth0 dashboard. Furthermore, you'll need to update your src/auth/Auth.js file. Replace the value of redirectUri with http://<your-url>/callback.

  • Now run npm run build in your terminal to build the project.

  • Go ahead and upload the files in the dist directory to your S3 bucket.

    Upload to S3

  • Wait about a minute. Now check out the app via your S3 endpoint URL. http://meetupservice.s3-website-us-east-1.amazonaws.com.

    Live App Live App working fine!

Let's take a break here! There are more platforms out there that we won't be able to cover such as Heroku, Microsoft Azure, Google Cloud, etc

There are several factors you consider before deploying JavaScript applications. Where do files such as Images, PDF, and Videos get stored? How do I set up monitoring and logging? How about SSL provisioning for my custom server? What if I don't want to set up my backend? Yeah, Serverless backends come in handy!

Let's briefly cover some of these questions posed above.

Serverless Backend

You might have heard about Serverless several times. It's a buzz word that's everywhere now. Serverless computing simply allows us to write functions as a service. No need for setting up servers yourself. There are tools and services that handle all the server provisioning for you. Basically, you run code without thinking about servers. Serverless offers you:

  • Auto Scaling.
  • Rapid deployment.
  • Pay only for the compute time you consume.

Services such as Webtask, AWS Lambda, IBM Cloud Functions, Google Cloud Functions, and Azure Functions allow you run a serverless backend.

There is a Serverless Application Framework that helps you build serverless architectures easily. It's powered by Serverless computing services such as Lambda, Azure Functions and IBM.

With the open-source CLI, you can get started on your machine ASAP!

Serverless Framework

Files Deployment

It's recommended that you use a dedicated service for handling files such as images, PDFs, documents and videos. As a developer, always offload these media assets to dedicated storage services. Services such as Cloudinary, Filestack, Uploadcare, Imgix and Fastly.

This is a feature by feature graphical comparison of the various services mentioned above with the features they offer.

File Deployment Services

Integrations

Cloudinary offers SDKs for different languages and frameworks, including Ruby on Rails, PHP, Angular, React, JQuery, Android, iOS, Python and JavaScript. Cloudinary also offers its services as add-ons or integrations on PaaS platforms, such as Heroku, Azure, EngineYard and AppFog.

Fastly does not provide any SDK. It simply operates via a query string URL API call.

Imgix offers SDKs for Ruby on Rails, Python, React, PHP, Node.js, Go, C# and Java.

Uploadcare offers SDKs for Ruby, Ruby on Rails, Python, PHP, Java, Angular, Android, iOS, and Meteor.

Production Monitoring & Logging

After deploying your app, the next step is to set up logs and monitoring for the application. Performance monitoring and logging allow you understand everything going on in your app. User interactions, application errors, and issues.

All these statistics offer you a better understanding of your app and userbase, which ultimately provides you and your team with better focus areas.

Fantastic monitoring and logging services you can integrate with your app are:

SSL

Every website should be secured to protect users' private information. An SSL Certificate is required to be installed on the server where the website is hosted. SSL certificates allow web servers to encrypt their traffic, and also offer a mechanism to validate server identities to their visitors. An SSL Certificate needs to be purchased from a trusted Certificate Authority such as GoDaddy, RapidSSL, Verisign, Digicert, etc.

SSLs aggregates several SSL providers and provides discounts on SSL purchase. However, Let's Encrypt is a Certificate Authority that offers free SSL certificates.

Let's Encrypt

Authorization - How It Works

Check out how to get started with Let's Encrypt.

Aside: Auth0 Authentication with JavaScript

At Auth0, we make heavy use of full-stack JavaScript to help our customers to manage user identities including password resets, creating and provisioning, blocking and deleting users. We also created a serverless platform, called Auth0 Extend, that enables customers to run arbitrary JavaScript functions securely. Therefore, it must come as no surprise that using our identity management platform on JavaScript web apps is a piece of cake.

Auth0 offers a free tier to get started with modern authentication. Check it out, or sign up for a free Auth0 account here!

Auth0 Login Page

It's as easy as installing the auth0-js and jwt-decode node modules like so:

npm install jwt-decode auth0-js --save

Then implement the following in your JS app:

const auth0 = new auth0.WebAuth({
  clientID: "YOUR-AUTH0-CLIENT-ID", // E.g., you.auth0.com
  domain: "YOUR-AUTH0-DOMAIN",
  scope: "openid email profile YOUR-ADDITIONAL-SCOPES",
  audience: "YOUR-API-AUDIENCES", // See https://auth0.com/docs/api-auth
  responseType: "token id_token",
  redirectUri: "http://localhost:9000" //YOUR-REDIRECT-URL
});

function logout() {
  localStorage.removeItem('id_token');
  localStorage.removeItem('access_token');
  window.location.href = "/";
}

function showProfileInfo(profile) {
  var btnLogin = document.getElementById('btn-login');
  var btnLogout = document.getElementById('btn-logout');
  var avatar = document.getElementById('avatar');
  document.getElementById('nickname').textContent = profile.nickname;
  btnLogin.style.display = "none";
  avatar.src = profile.picture;
  avatar.style.display = "block";
  btnLogout.style.display = "block";
}

function retrieveProfile() {
  var idToken = localStorage.getItem('id_token');
  if (idToken) {
    try {
      const profile = jwt_decode(idToken);
      showProfileInfo(profile);
    } catch (err) {
      alert('There was an error getting the profile: ' + err.message);
    }
  }
}

auth0.parseHash(window.location.hash, (err, result) => {
  if (err || !result) {
     // Handle error
    return;
  }

  // You can use the ID token to get user information in the frontend.
  localStorage.setItem('id_token', result.idToken);
  // You can use this token to interact with server-side APIs.
  localStorage.setItem('access_token', result.accessToken);
  retrieveProfile();
});

function afterLoad() {
  // buttons
  var btnLogin = document.getElementById('btn-login');
  var btnLogout = document.getElementById('btn-logout');

  btnLogin.addEventListener('click', function() {
    auth0.authorize();
  });

  btnLogout.addEventListener('click', function() {
    logout();
  });

  retrieveProfile();
}

window.addEventListener('load', afterLoad);

Get the full example using this code.

Go ahead and check out our Quick Start tutorials to learn how to implement authentication using different languages and frameworks in your apps.

Conclusion

There is no way we can cover all the different options available for deploying JavaScript applications. JavaScript is a language that has evolved over the years, from simple fancy scripts to running on servers.

Hopefully, this guide covers a lot of your needs for deploying your JavaScript applications to all the major cloud providers.

Auth0 provides the simplest and easiest to use user interface tools to help administrators manage user identities including password resets, creating and provisioning, blocking and deleting users.

How have you been handling your deployments? Please, let me know in the comments section below!