As always, when I blog about stuff like this, I want to remind folks I'm both new to serverless and new to OpenWhisk, so while what follows works, I'm not necessarily saying it is the best way to do things, or even a good idea. But let's be honest, that's never stopped me before, right?

So the question I had this morning was - given an OpenWhisk action built for Web Action support, how would you make use of a templating engine to help return an HTML-based response?

My preference for templating engines is Handlebars (although you can read about more options in my book), so I looked at that first. Here is the sample action I came up with.

const Handlebars = require('handlebars');
const fs = require('fs');

let template = '';

function main(args) {

	if(template === '') {
		template = Handlebars.compile(fs.readFileSync(__dirname+'/templates/cats.html', 'utf-8'));
		console.log('loaded template from fs');
	}

	let data = {
		title:"A list of cats",
		cats:[
			"Lilith",
			"Sinatra",
			"Luna",
			"Pig",
			"Cracker"
		]
	}

	let html = template(data);

	return { body:html };
}

exports.main = main;

So, the basic idea is this:

  • See if we have a template variable setup, and if not, do a file read of our template and create the Handlebars function for it. You may be wondering how that works considering the action is run in an atomic fashion with no real persistence. While that is true, a "warmed up" action will be kept alive. This makes simple caching like this ok. (I talked about this more here: Serverless and Persistence).
  • I then get my data, which in this case is hard coded. Obviously I could use arguments passed to the action, make data on the fly, etc, but a simple hard coded object was enough.
  • I then generate the HTML using the Handler's compiled function, template. (Not a terribly descriptive name. I apologize.)
  • Finally, I return the generated HTML.

The template is rather simple:

<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>{{title}}</title>
		<meta name="description" content="">
		<meta name="viewport" content="width=device-width">
	</head>
	<body>

		<h2>{{title}}</h2>
		
		<p>
			I'd like to thank the following cats for making everything possible:
		</p>

		<ul>
			{{#each cats}}
			<li>{{.}}</li>
			{{/each}}
		</ul>
	</body>
</html>

The final bit is to simply create the action. I'm using a custom npm module so I had to zip it up, but I just used a shell script again:

#!/bin/bash
zip -rq temp.zip test1.js package.json node_modules templates
wsk action update templatetest/test --kind nodejs:6 temp.zip --web true
rm temp.zip

Note the --web true at the wsk call. This lets OpenWhisk know this is a Web Action. Finally, you can see the result here:

https://openwhisk.ng.bluemix.net/api/v1/web/rcamden@us.ibm.com_My%20Space/templatetest/test.http

Some thoughts:

  • If I was really going to use one template, I'd probably consider just using a template string in the action code. I feel like that would be a bit messy though, and I'd not get my nice HTML syntax coloring/autocomplete in my editor. But I'd consider it for sure.
  • And of course, don't forget that template strings support variable substitutions, like a "lite" version of a template engine like Handlebars. But you don't get stuff like conditionals and loops though.
  • Handlebars supports compiling template strings into functions and saving them as well. So a better approach would be to do that, require them into the action, and deploy that. You could use your shell/bat script to do all of this at once.

Any comments, or concerns, about this approach?