Building static websites with Azure Functions, mustache & Azure Storage

In this post I will show you how to turn some dynamic data into a static website using a timer-triggered Azure Function (written in nodejs) & mustache with static website hosting for Azure Storage.

For this demo I’ll create a simple, personalized start page showing some headlines I grab from a WordPress site and also adding a few links I often use. There are certainly way more interesting and useful scenarios for this but it should help you get started with your own ideas.

These are the steps we want to accomplish:

  • Prepare the mustache templates
  • Get the template files and “compile” them for further usage
  • Load the data we want to display
  • Render the html and upload it to Azure Storage

The Mustache Templates

To showcase how you can split your templates into several files I created an index, a header and a footer file. By calling {{> header.mustache}} and
{{> footer.mustache}} inside the index these will be merged into one file.

For the news feed we’ll loop through an array of articles, showing the title and adding a link to the page. This is what the file then looks like:

{{> header.mustache}}
<h1>WindowsArea.de News</h1>
<ul>
{{#news}}
<li><a href="{{link}}" target="_blank">{{title.rendered}}</a></li>
{{/news}}
</ul>
<h1>My Links</h1>
<ul>
{{#links}}
<li><a href="{{url}}" target="_blank">{{url}}</a></li>
{{/links}}
</ul>
{{> footer.mustache}}
view raw index.mustache hosted with ❤ by GitHub

These three files will be put into their own Azure Blob Storage “templates” directory. This way they can be updated independently of the Azure Function code. Also, we can use bindings to read them inside the Function code and don’t have to manually download them.

[fusion_builder_container hundred_percent=”yes” overflow=”visible”][fusion_builder_row][fusion_builder_column type=”1_1″ background_position=”left top” background_color=”” border_size=”” border_color=”” border_style=”solid” spacing=”yes” background_image=”” background_repeat=”no-repeat” padding=”” margin_top=”0px” margin_bottom=”0px” class=”” id=”” animation_type=”” animation_speed=”0.3″ animation_direction=”left” hide_on_mobile=”no” center_content=”no” min_height=”none”]

A view at the “templates” directory in Azure Blob Storage using Azure Storage Explorer

The Static Website Generating Function

As stated above we’ll be writing our logic in JavaScript. I’ve created a new Function inside Visual Studio Code and added a few npm packages that we’ll be needing:

  • request – to download the dynamic content
  • mu2 – a Node.js Mustache engine
  • azure-storage – the SDK to upload our finished html files to Azure Blob Storage

So now let’s get that data and store it in the mynews variable:

function getData(){
context.log('get data');
var options = {
url: 'https://windowsarea.de/wp-json/wp/v2/posts',
method: 'GET',
json: true
}
request(options, function (error, response, data) {
context.log("request callback");
if (error) {
context.log(error);
}
else {
context.log(data.length);
mynews = data;
}
prepareTemplates();
});
}
view raw getData.js hosted with ❤ by GitHub

Since we’re setting json to true in the options-object, the response string will be automatically parsed to an object or in this case an array of posts.

After that we’re calling the prepareTemplates() function which will prepare both the header and footer files for later usage. We can access the templates directly from the context.bindings object. Have a look at the functions.json file for how the bindings are configured.

function prepareTemplates(){
context.log('prepare Templates');
mu.compileText('header.mustache', context.bindings.headerTemplate, function () {
mu.compileText('footer.mustache', context.bindings.footerTemplate, function () {
renderSite();
});
});
}

When this is done we can actually render our index site with the content and templates we’ve prepared. We’ll pass our data to the mu.render() function and save the stream output to the pageFile variable. This will now be our html code stored as a string.

function renderSite(){
context.log("render site");
var pageFile = "";
mu.compileText('index.mustache', context.bindings.indexTemplate, function (err, parsed) {
var renderstream = mu.render(parsed, { news: mynews, links: mylinks });
renderstream.on('data', function (data) {
pageFile += data;
});
renderstream.on('end', function (data) {
context.log("done creating index.html");
uploadSite(pageFile);
});
});
}
view raw renderSite.js hosted with ❤ by GitHub

After the stream has finished we’ll upload the result to Azure. Technically we could to this using Azure Function’s output bindings. However, that way we don’t have control over the file’s content type which will cause issues in some browsers.

But first we make sure the $web container exists and then create a new Blob from our html string. Here we can also set the correct content type which is ‘text/html’.

function uploadSite(page){
context.log("Upload site ");
var blobService = storage.createBlobService();
// create $web container
blobService.createContainerIfNotExists('$web', function(){
// upload index.html to $web container
const options = { contentSettings: { contentType: 'text/html' } }
blobService.createBlockBlobFromText("$web", "index.html", page, options, function (error) {
context.log("uploaded");
context.done();
});
});
}
view raw uploadSite.js hosted with ❤ by GitHub

And we’re done. You can find the full code on Github. The final result can be seen here (I know, I didn’t put much time into styling this…).

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.