Back to previous page

Build Your First Serverless Web Application on Azure

This is a tutorial for absolute beginners.

Whether you’re a developer or an operations engineer, it’s a good idea to start wrapping your brain around the fundamental concepts of serverless computing. The technology is still in its infancy, but I believe it will radically shift how many applications are built in the years to come.

In this guide, I’ll walk you through setting up a super basic serverless web application on Azure. Just a simple webform that sends and receives data to and from an Azure Function.

The HTML page for this serverless application will be hosted in an Azure Storage account. Users will visit the webpage and submit a message. The form will trigger an Azure Function that will send a message back to the user. There will be no virtual machines involved. Just code. Hence the term serverless 😉

Here’s the good news; you don’t need to be an experienced programmer to follow along with this tutorial. I’ll show you how to build the resources you’ll need in Azure, and I’ll give you some sample code to try out.

Sound good? Let’s go.

Before We Begin

We need to build a handful of Azure resources to support both the front-end and the back-end of the application. We’ll create an Azure Storage account, build an Azure Function, and then deploy and test a simple HTML web page.

Yes, we could automate all of this work, but that’s a topic for another post. My goal here is to help you grasp the concept of a serverless web app using a simple example with minimal steps.

However, we will use the Azure CLI as much as possible as we build the infrastructure. I’ll try to minimize the steps required in the portal since the UI tends to change frequently.

Don’t worry about installing the Azure CLI locally unless you want to. You can use shell.azure.com to run the CLI in a web browser, or you can run it inside VS Code.

Step 1. Building the Azure Storage Account

Fire up the Azure CLI. If you have more than one Azure subscription you should make sure your CLI session is set accordingly. You can get a list of your subscriptions and their id’s by running:

If you have more than one subscription, use the output from the above command to scope your CLI session to the desired subscription id using the syntax below:

Next, create a resource group to store the resources for the application. If you’re not sure what the region names are you can use the “az account list-locations” command. I’m using westus2.

Now we are ready to create the Storage Account. Make sure you use a globally unique DNS-compliant name for the storage account using the –name flag. Also note that only GPv2 storage accounts allow you to serve static content like HTML, CSS, JavaScript, and images. Therefore, the –kind flag must be set to StorageV2 as shown below.

Now that the storage account is created, we need to enable static website hosting. As I am writing this article, static website hosting for GPv2 storage accounts is generally available, but we still need to install the storage preview extension to enable it via the CLI. Run the following command to install the extension:

The final step is to enable static website hosting. Make sure to update the –name flag to reflect your storage account name:

If you run into problems creating the storage account or enabling static website hosting from the CLI, you can do this work through the Azure portal using the steps outlined in this guide in the Azure documentation.

Here’s what you should see in the portal after running the above CLI commands or manually enabling static website hosting:

Figure 1. Reviewing the static website properties.

Figure 1. Reviewing the static website properties.

Notice that the default document is set to index.html. That file doesn’t existing yet. We’ll deploy it later. Go ahead and record the primary endpoint URL for the static website as shown in Figure 1 becuase we’ll need this value in the next step.

Step 2. Building the Azure Function App

Now it’s time to create a function app. Think of a function app as a resource that hosts the execution of one or more serverless functions. The way the function app is configured will be unique for each application becuase it controls the underlying OS platform and runtime stack. The function app settings also control the billing model you’ll use for your serverless functions.

Take a look at the CLI command below. You can see we’ll be using the az functionapp create command to build the function app. The consumption plan location defines where our code will run and how we’re billed. Azure Functions consumption plan is billed based on per-second resource consumption and executions and includes a monthly free grant of 1 million requests. Also, pay close attention to the –name flag. Like Azure Storage, the name must be a DNS-compliant globally unique value as it will be used as the host portion of the URL for the function app. We’ll set the runtime node. Make sure you update all the values in the example below and then run the command to create the function app.

After the function app has been created we need to update the Cross-Origin Resource Sharing (CORS) configuration. CORS is a mechanism that tells a web browser connected to one origin (domain) that its OK to access resources on another server.

Here’s why this is important; we’re going to have a static HTML page in the Azure Storage account acting as the front-end of the serverless application. There will be JavaScript in that page that executes when users fill out and submit a form, and that client-side code will be connecting to a different endpoint (the function URL) to communicate with the back-end of the application. CORS allows us to define where that traffic is allowed to originate from (in this case, the static website in Azure Storage), and it will tell web browsers used by our clients that its not a security risk to do this.

So, what we need to do is run the az functionapp cors add command as shown below. We’ll need to target the previous resource group and function app resources we created, and also define the allowed origin which will be the primary endpoint URL from the Storage Account from Step 1.

Once the CORS configuration has been set you’ll be able to see the change in the platform features section of the function app in the Azure portal, as shown in Figure 2.

Figure 2. Reviewing the CORS configuration.

Figure 2. Reviewing the CORS configuration.

Again, if you run into any problems doing this from the CLI, you can manually create the Function App and add the static website primary endpoint URL to the CORS configuration.

Step 3. Creating an Azure Function

Now that we have a function app, we can create a new function. While the Azure CLI makes it easy to create the function app from the command-line, we need to use other tools to create our functions. One way we could do it is by using the Azure Core Tools, but I’m not going to get into that here. Instead, we’ll create the function inside the Azure portal.

Navigate to your function app and highlight Functions. Click the New function button to start the process as shown in Figure 3.

Figure 3. Locating the new function button.

Figure 3. Locating the new function button.

Next, we can select a template for our function. Select the HTTP trigger template as shown in Figure 4.

The last step is to name the function and set the authorization level. Change the name of the function to SayHello and set the authorization level to Anonymous.

Figure 5. Setting the function name and auth level.

Figure 5. Setting the function name and auth level.

After the function has been created you’ll see the default code for the HTTP trigger template as shown below in Figure 6.

module.exports = async function (context, req) {
context.log(JavaScript HTTP trigger function processed a request.);
if (req.query.name || (req.body && req.body.name)) {
context.res = {
// status: 200, /* Defaults to 200 */
body: Hello + (req.query.name || req.body.name)
};
}
else {
context.res = {
status: 400,
body: Please pass a name on the query string or in the request body
};
}
};
view rawindex.js hosted with ❤ by GitHub
Figure 6. Reviewing the Function App Settings.

This is the exported JavaScript function that will execute when triggered via HTTP. Let’s break it down a bit so we know whats going on when we call the function.

  • Line 1: You can see the first argument sent to the function is a context object which, among other things, is used for communicating with the Azure Functions runtime. The second argument (req) is the request object that contains the details about what was sent to the function. We’ll be able to use the req object to access the data sent to this function from our web form.
  • Line 4: Here we’re using some conditional logic to make sure the request contains a name key within the query string parameters or request body.
  • Line 5: If a name was provided with the request, we use the context.res object to send a response. In this case, we send back a “hello” message to the caller.
  • Line 11: If a name wasn’t provided, we’ll send back an HTTP 400 error with a message explaining what the problem is.

While you’re viewing this code in the Azure portal, you’ll see a link at the top of the screen that says Get function URL as shown in Figure 7.

Figure 7. Copying the function URL.

Figure 7. Copying the function URL.

Copy the URL from this screen as we’ll need it for the next step.

Step 4. Configuring the HTML Front-end

Now we are ready to configure the HTML page for the serverless application like the one shown in Figure 8.

Build Your First Serverless Web Application on Azure

This page uses Bootstrap 4 which is an easy to use open source toolkit for developing with HTML, CSS, and JavaScript. Let me point out some of the basics that are going on inside the HTML code for this form that is relevant to the functionality of our serverless application.

Check out the snippet from Figure 9:

<div class=container>
<h1 class=display-4 mb-4>What’s your name?</h1>
<hr>
<div id=output></div>
<form id=submitMessage>
<div class=form-group>
<input type=text id=name class=form-control placeholder=Type your name…>
</div>
<input type=submit class=btn btn-secondary value=Submit>
</form>
</div>
view rawform.html hosted with ❤ by GitHub
Figure 9. The HTML form for the app.

Here’s what you need to know:

  • Line 4: There’s an empty output div we can update based on the response from our Azure function. We’ll use this element to provide a success or failure message to the user.
  • Line 7: The input field where the user submits their name. Notice that the id is set to name. We’ll grab the value from this input and send it along with the request to our Azure Function.
  • Line 9: When the user clicks the submit button we’ll post the form to our Azure function.

Now that you understand what is going on with the HTML code, let me explain a couple of points about the client side javascript that will send the form data to our serverless function.

Take a look at the code in Figure 10:

function submitMessage(e) {
e.preventDefault();
let name = document.getElementById(name).value;
fetch(<YOUR FUNCTION URL GOES HERE>, {
method: POST,
headers: {
Accept: application/json, text/plain, */*,
Content-type: application/json
},
body: JSON.stringify({
name: name
})
})
.then((res) => processResponse(res))
}
view rawsubmitMessage.js hosted with ❤ by GitHub
Figure 10. The client side JavaScript that calls our serverless function.

This is a vanilla JavaScript function that will be called when the user clicks the Submit button. Here are some important points about the code in Figure 10:

  • Line 4: We grab the value of the input field in the HTML form.
  • Line 5: The Fetch API will be used to facilitate the request/response process. You’ll obviously need to update this line with your own function URL.
  • Line 12: The value for the name input will be sent as part of the body of the POST request.
  • Line 15: fetch() returns a promise containing the response, and that gets passed to another client side function called processResponse() which will update the UI.

Still awake? Good 🙂

All of this code is included in a single HTML file for the sake of convinience. Grab the Index.html page from GitHub. Don’t forget to replace “<YOUR FUNCTION URL GOES HERE>” with the URL of your function that you recorded in the Step 3.

Step 5. Deploying the HTML Front-end

Let’s take the index.html file and upload it into our Azure Storage account. Since static website hosting is already enabled for the storage account, we simply need to upload the file/blob to the $web container.

This can be done easily in the portal, but here’s how you can do it via the Azure CLI:

Notice that I had to escape the “$”” character with “\” when executing the CLI in bash. Also note the the –source flag expects a directory since this command is designed to upload multiple files as a batch process. This makes sense as most sites will have multiple HTML, CSS, JavaScript, and image files that need to be uploaded.

Step 6. Testing the Serverless Web Application

Finally, its time to test our serverless application. Navigate to the static website primary endpoint URL to view your application. After filling out and submitting the form, you should see a response similiar to the one in Figure 11.

Figure 11. The UI for our serverless app in action.

Isn’t that awesome?

You might be wondering how the success message was created on the page. That work is done as part of the response where we call the processResponse() function shown in Figure 12.

function processResponse(response) {
if (response.status === 200) {
output =
<div class="alert alert-success" role="alert">
Hello, ${document.getElementById('name').value}! It's nice to meet you!
</div>
;
document.getElementById(output).innerHTML = output;
} else {
output =
<div class="alert alert-danger" role="alert">
Oh no! Something went wrong :(
</div>
;
document.getElementById(output).innerHTML = output;
}
}
view rawprocessResponse.js hosted with ❤ by GitHub
Figure 12. The processResponse() function.

What we’re doing with this function is checking the HTTP response code sent back from the Azure function. Regardless of the result, we use processResponse() to update the output element on the page. We’re taking advantage of Bootstrap alerts to add some flare.

Here’s what it would look like if there was a problem:

Figure 13. The UI for our serverless app when there is an error.

This is a good example of how we can give feedback to users based on the result of a function invoked over HTTP. Obviously we could do client-side validation and more error handling on the back-end but I think you get the idea.

Where to go from here

We’ve only scratched the surface, but I hope this tutorial has given you a little more perspective. Going forward, I’d recommend these resources from Microsoft to learn more about serverless on Azure:

About the Author:

I’m a consultant, advisor, author, and mentor for people ramping up on cloud-based technologies.

I got a break in the late 1990’s when someone took a chance on me. Although I had no experience, I landed my first job in the tech industry. I promised myself I wouldn’t waste the opportunity.

Fast forward to today, and I’ve worked for tech giants like Microsoft and Amazon, and have founded two consulting companies of my own. I’ve been paid to speak on stage at events around the world. I’ve published four books and have created over twenty online courses that have reached more than 300,000 learners on platforms including Pluralsight, Lynda.com, and LinkedIn Learning.

I’ve learned firsthand that anyone can create a successful career in the tech industry. My goal is to shorten that learning curve for others and I am a big believer in contributing to the technical community. I’m proud to be recognized as a Microsoft Azure MVP, and I am contributor for several online publications including ForbesMedium, and TechTarget.

Every week I share technical tips and career advice for people working in the cloud computing industry though my podcast and email newsletter.

Reference:

Pfeiffer, M. (2018). Build Your First Serverless Web Application on Azure. Available at: https://mikepfeiffer.io/azure-serverless-101.html [Accessed: 28 January 2019]

Share this on...

Leave a Reply

Back to previous page