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
- An IBM Cloud account (register for your free trial account or log in to IBM Cloud if you already
have an account)
- A working knowledge of HTML and JavaScript
- A working knowledge of the MEAN application stack (at least
Angular)
(If you're not familiar with MEAN, you can get started with "Build a self-posting Facebook application with IBM Cloud and the MEAN stack," a three-part tutorial series on IBM developerWorks.)
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:
- Go to the OpenWhisk console in IBM Cloud.
- Log in to IBM Cloud and click Start creating to use OpenWhisk from your browser and enter the editor.
- Click Develop on the left sidebar.
- Click Create Action.
- Create a new action with these parameters:
Field Value Name trivial Execution runtime Node.js 6 Select a sample (or blank) Start with a Blank Slate - 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, andjs
, which contains JavaScript code to run on the browser. Replace the JavaScript code with this:
/** * * main() will be invoked when you Run This Action. * */ function main(params) { var name; name = params.name; if (name == undefined) name = ""; return { html: "<b>" + JSON.stringify(params) + "</b>", js: "alert('hello " + name + "');" }; }
- Click Run This Action.
- Add a name field to the input:
{ "message": "choose a value to pass to My New Action", "name": "Ori" }
- Click Make it Live and then Run with this Value.
- See that the output contains HTML and JavaScript:
- 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:
- Click APIs on the left sidebar.
- Click Create an OpenWhisk API.
- Create an API with these parameters:
Field Value API name User-facing application Base path for API /api/userfacing Enable CORS so that browser-based applications can call this API Selected - 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.
- Click Definition on the left sidebar.
- Click Create operation.
- Create a new operation with these parameters:
Field Value Path /trivial Verb GET Package containing action Default Action Trivial Response content type application/json - Click Save. Then, scroll down and click Save.
- Click Summary on the left sidebar.
- Click the URL under "Route." It should fail with this error message:
- Add
/trivial
to the end of the URL. You should get back a JSON structure with HTML and JavaScript: - 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
response.data
, 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 response.data.html
, 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(response.data.html);
The eval
function gets the JavaScript code in
response.data.js
, and executes it.
eval(response.data.js);
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.

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
.
Conclusion
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.