Build a user-facing OpenWhisk application with IBM Cloud and Node.js

Implementing a user interface with serverless lambda functions

OpenWhisk is a cloud-first distributed event-based programming service that is integrated with IBM Cloud®. OpenWhisk uses a serverless deployment and operations model that hides infrastructure complexity and lets you focus on the code you want to execute.

Even though OpenWhisk is designed for internal processing, it offers several benefits for user-facing applications. When you combine it with a static file, you can use it to serve applications that are relatively easy to debug. And because OpenWhisk applications are a lot less computationally intensive than running a server process on a PaaS platform, they are considerably cheaper, as well.

Another benefit of using OpenWhisk is its modular architecture, which lets you implement your application as a collection of microservices. You can easily modify parts of the user interface without changing the rest of it, or reuse panels in multiple applications.

In this tutorial, we'll start by writing a trivial "Hello OpenWhisk" application. Then we'll expand that application into the start of a more useful one, a multi-language phrase book.

What you'll need to build your OpenWhisk application

A trivial app: "Hello OpenWhisk"

To build a user-facing OpenWhisk application, let's first build a trivial OpenWhisk microservice and make it usable from a web browser. Click here to see it in action.

Create a trivial OpenWhisk action

To create an OpenWhisk action:

  1. Go to the OpenWhisk console in IBM Cloud.
  2. Log in to IBM Cloud and click Start creating to use OpenWhisk from your browser and enter the editor.
  3. Click Develop on the left sidebar.
  4. Click Create Action.
  5. Create a new action with these parameters:
    Execution runtimeNode.js 6
    Select a sample (or blank)Start with a Blank Slate
  6. Click Create Action.

    By default, OpenWhisk actions are JavaScript. They receive an associative array in their input and return another as their output. For our purposes, the response will have two fields: html, which contains HTML code to display, and js, which contains JavaScript code to run on the browser.
  7. Replace the JavaScript code with this:

    	  * main() will be invoked when you Run This Action.
    	function main(params) {
    		var name;	
    		name =;
    		if (name == undefined)
    			name = "";
    		return {
    			html: "<b>" + JSON.stringify(params) + "</b>",
    			js: "alert('hello " + name + "');"
  8. Click Run This Action.
  9. Add a name field to the input:
    	    "message": "choose a value to pass to My New Action",
    	    "name": "Ori"
  10. Click Make it Live and then Run with this Value.
  11. See that the output contains HTML and JavaScript: Output with HTML and JavaScript
    Output with HTML and JavaScript
  12. Click Close.

Create an OpenWhisk API

To make it easy to access the OpenWhisk action from the outside, for example from a web browser, we create a publicly accessible API:

  1. Click APIs on the left sidebar.
  2. Click Create an OpenWhisk API.
  3. Create an API with these parameters:
    API nameUser-facing application
    Base path for API/api/userfacing
    Enable CORS so that browser-based applications can call this APISelected
  4. Click Save. We could have created operations during this step, but I prefer to explain how to do them independently because you need to do that later in the article.
  5. Click Definition on the left sidebar.
  6. Click Create operation.
  7. Create a new operation with these parameters:
    Package containing actionDefault
    Response content typeapplication/json
  8. Click Save. Then, scroll down and click Save.
  9. Click Summary on the left sidebar.
  10. Click the URL under "Route." It should fail with this error message: Error message
    Error message
  11. Add /trivial to the end of the URL. You should get back a JSON structure with HTML and JavaScript: JSON structure with HTML and JavaScript
    JSON structure with HTML and JavaScript
  12. Copy the URL under "Route" to a text file. You will need it soon.

Browser-side code

In the past, it was necessary to use a Node.js application as a conduit to OpenWhisk. But now that APIs can have CORS (cross-origin resource sharing) enabled, all you need is an index.html file that can come from anywhere.

All you need to do is download the file from GitHub, modify the value of the owUrl variable in line 21 to the "Route" value, and open the file in a browser. It will call the trivial OpenWhisk action, display the HTML in the response, and run the JavaScript

The browser-side code uses the Angular library (the "A" in MEAN stack). That library comes with a service called $http that allows the JavaScript code in the browser to act as a web client.

The function that performs most of the work is $scope.getHtml. This function appends the name of the action to the route URL and attempts to get it.

$http.get(owUrl + "/" + action)

If the request is successful, the result will be in, already parsed (correctly, as JSON). This code uses jQuery. $("# " + tag) searches for an HTML tag whose id attribute is tag. Then, the .html(<string>) method sets that tag's HTML to the string, in this case, the result from OpenWhisk. Note that this is normally a risky operation. But it's acceptable here as long as we make sure the HTML we send from OpenWhisk is safe.

          		.then(function(response) {
          			$("# " + tag).html(;

The eval function gets the JavaScript code in, and executes it.


A more useful app: Phrase translator

Now that we have a trivial "Hello OpenWhisk" application running, let's turn it into a useful application. This application lets you select a word in English, and gives you that same word in multiple languages. The application's list of words is hard wired for the sake of simplicity. If you want to use a database, you do it the same way you would with a Node.js application.

Create two panels

In this application we use two panels—a side panel for navigation and a main panel to show information. These two panels are specified in the panels action in OpenWhisk. They are placed side-by-side using the Bootstrap framework.

function main(params) {// Store  the HTML to send in this variable
	var html = "";

	// The two main panels
	html = '<div class="col-md-3" id="side"></div>' +
		'<div class="col-md-9" id="main"></div>';
	// The row-fluid
	html = '<div class="row-fluid">' + html + "</div>";
	// The container-fluid
	html = '<div class="container-fluid">' + html + "</div>";

Note that the two panels have id attributes. The JavaScript code returned by the action calls getHtml to fill those two panels.

  return {
        "html": html,
        "js": '$scope.getHtml("side", "sidepanel"); $scope.getHtml("main", "mainpanel");'

After you create the action and make it live, add to the API a new operation with the path /panels to call it.

Add a new operation with the /panels path to the API
Add a new operation with the /panels path to the API

Create the side panel

The side panel displays a list of words as buttons. When any of them is pressed, it uses the getHtml function to fill the main panel with information about that word. This is the action:

function main(params) {

	var words = ["Hello", "Goodbye", "Thanks", "You're welcome"];
	var html = "";
	for (var i=0; i<words.length; i++) {

This code is outside the Angular scope, so it cannot call $scope.getHtml. To solve this, the stub creates a global variable called scope and assigns it the value of $scope. That function itself is within the Angular scope, so it can use $http. The word itself is escaped to allow for expressions that contain apostrophes, such as "You're welcome."

The query is used to as part of the action as an easy way to transfer a parameter to the main panel action. It appears in the input, as you will see in the next section.

		var onClick = "scope.getHtml('main', 'mainpanel?word=" + escape(words[i]) + "')";
		html += '<p><button type="button" class="btn btn-info" onClick="'
			+ onClick + '">' + words[i] + '</button></p>';

The return value does not contain JavaScript, because there is no JavaScript we need to run when the HTML is displayed.

	return { "html": html };

Add this action to the API as /sidepanel.

Create the main panel

The main panel shows a word and all of the translations known to the program. It uses the parameter passed to main to get the word, and builds up the HTML based on a hash table. Again, it does not need to tell the browser to run any JavaScript.

function main(params) {
	var words = {
		"Hello" : {
			Hebrew: "Shalom",
			Spanish: "Hola",
			Arabic: "Marchaba"
		// The full list of words is in GitHub
	var html = "";
	// Heading
	html += "<h2>" + params.word + "</h2>";

This parameter comes from the query part of the URL that's used by the side panel.

	var list = words[params.word];
	if (list == undefined)
		html += "I am not familiar with this word, sorry";
	else {
		var langs = Object.keys(list);
		html += '<table class="table table-striped">';
		for (var i=0; i<langs.length; i++)
			html += '<tr><th>' + langs[i] + '</th><td>' + list[langs[i]] + '</td></tr>';
		html += "</table>";	

	return { "html": html };

Add this action to the API as /mainpanel.

Modify index.html

Finally, change line 41 in index.html to get panels instead of trivial.


I hope these simple applications have given you a feel for the power of OpenWhisk. The OpenWhisk platform allows you to develop and execute logic in response to events in applications without having to worry about infrastructure.

OpenWhisk is available as a IBM Cloud service and as an open source project on GitHub to encourage developers to accelerate its development and to generate a powerful ecosystem of event providers and consumers.

Downloadable resources

Related topics

Zone=Cloud computing, Open source
ArticleTitle=Build a user-facing OpenWhisk application with IBM Cloud and Node.js