Serving faster, agile serverless web pages

Dror Gensler
4 min readJul 10, 2018

Beyond acting as API endpoints or gluing together back-end services, serverless (FaaS) functions are also capable of serving web pages.

In this article we will review an alternative method for serving web pages using serverless functions — one that utilizes the asynchronous nature of web applications and a serverless architecture.
Apart for accelerating serverless web pages, this approach also enables a new, unique feature which as far as I know is available only in serverless —
“dynamic static content”.

But we’ll get back to this oddity later :) Lets begin with a quick recap:

In my previous article I created a “Serverless, DB-less blog”, demonstrating how serverless functions can generate HTML, CSS and Javascript on the fly and serve it to the client.
I created individual functions for each resource:

Then exposed them as a public endpoint using the FunctionFactory.io web interface:

When a user requested the main index page, the function behind it used Python to generate the required HTML structure.
It then went on to call additional functions and append their output to our page template, adding new features — a navbar, and “storage functions” that held and returned the actual content of our blog, one for each post.

However, preparing the content in this manner has some drawbacks, chief among them the long wait times caused by function cold-start and spin-up times. The user had to wait for the index function to finish, but it wont until each of its “child” functions had returned its output.
The result is spin-up times aggregating to unacceptable levels- over 1 second for a simple blog, and it will be much worse should we add more content and components.

This time we’ll do it right — calling each function independently.
You can see the end result here:
https://functionfactory.io/api/v1/web/drors/private/modIndex

First I created the main index function:

def main(params):

body = '''<html>
<head>
<link rel="stylesheet" type="text/css" href="https://functionfactory.io/api/v1/web/drors/private/modStyle">
</head>

<body>

<div class=main>
<h1>Hello!</h1>
</div>

<div id="sidebar"></div>

<script src="https://functionfactory.io/api/v1/web/drors/private/modSidebar">
</script>

</body></html>'''

return {'body': body}

This simple Python function returns plain HTML as as “body”, which the FunctionFactory / OpenWhisk platform sends to the browser as a web page.

The CSS stylesheet is also a simple function returning static content:

def main(params):

body = '''<style>
body {
font-family: “Lato”, sans-serif;
}
.sidenav {
height: 100%;
width: 160px;
position: fixed;
z-index: 1;
top: 0;
left: 0;
background-color: #111;
overflow-x: hidden;
padding-top: 20px;
}
.
.
</style>'''
return {'body': body}

The sidebar is where things get interesting..
We need a way to append the HTML elements served to the browser by the main “modIndex” function.
To that end I created <div id="sidebar"> , an element which will be replaced with the sidebar HTML using document.getElementById("sidebar").innerHTML .

After setting the placeholder div I call the “modSidebar” function which contains both the sidebar content and the replacing script:

import astdef main(params):    // Prep structure of sidenav
sidenav = '<div class="sidenav">'

// Append sidenav items
defaultItems = '[("title","https://functionfactory.io")]'
items = params.get("sideNavItems", defaultItems)
items = ast.literal_eval(items)
for item in items:
sidenav += '<a href="'+item[1]+'">'+item[0]+'</a>'
sidenav += '</div>'


sidebar = '''// select the element that will be replaced
var el = document.getElementById('sidebar');
// Create a new element to place sidebar in
var newEl = document.createElement('p');
newEl.innerHTML = `<meta name="viewport" content="width=device-width, initial-scale=1">{sidenav}
`
// replace el with newEL
el.parentNode.replaceChild(newEl, el);
'''.format(sidenav = sidenav)
return {'body': sidebar}

This might seem a bit confusing, but what I’m essentially doing is:
a. prep “sidenav” html content using Python
b. prep “sidebar” Javascript that will replace the placeholder div in the index
c. Place the “sidenav” items into the sidebar innerHTML using Python .format — sort of a “search & replace”.

Now the output of “modSidebar” function is returned as plain Javascript, which browser will execute and replace the placeholder div with actual sidenav content.

The browser is oblivious to the fact that the script passed to it with <script src= is actually dynamically generated. It receives the final Javascript.

And this brings us to the “oddity” —
As the above function preps content dynamically, we can actually pass parameters to this script src import to alter the returned Javascript -
All I need to do is pass parameters in the src= URL of the script call in the main index function, just like I would to any other API endpoint or URL:

<script src="https://functionfactory.io/api/v1/web/drors/private/modSidebar?sideNavItems=[(\'Home\',\'https://functionfactory.io/home\'),(\'About\',\'https://functionfactory.io\')]"></script>

This is interesting because it allows us to create “generic” web page components, like our sidebar, and reuse them with different content outputted based on parameters with which they are called.

With such capability it is possible to share these web components for others to consume, like in the FunctionFactory component library.

I hope you find this interesting and not too confusing, and like me see the great potential of serving web pages using serverless functions.

These components are available for consumption and alteration within the FunctionFactory.io component library.

--

--