Cloud Functions Package Design and Best Practices

By: Van Staub and Belinda Vennam

IBM Cloud Functions and installable packages

Whether they’re JARs, libs, Gems, or modules, successful programming languages provide an ability to share and re-use code. IBM Cloud Functions is no different—it enables developer productivity through installable packages.

The companion app that is linked at the end of this “Reviewing IBM Cloud services, resources, and usage” article provides several new packages—notably for SQL Query and Cognos Dashboard Embedded. Having created these packages, I can now consider the question: “What makes a good package?

Delivery

Arguably the most important aspect of a package utility is easily finding and installing code. IBM Cloud Functions uses wskdeploy to define which artifacts are installed—from Actions to Sequences and even managed APIs. To include other packages as dependencies, developers need only add a statement in their manifest.

dependencies:
cloud-object-storage:
location: github.com/ibm-functions/package-cloud-object-storage/runtimes/nodejs

As you can see from the location property, the package is hosted directly from GitHub. So your code, documentation, and delivery can all be done from one place. To make finding packages easier, add the topic openwhisk-packages to your repository.

Composition

Packages deliver functionality. You can leverage package artifacts—such as an Action—by chaining them together into a Sequence. For example, a common use case for SQL Query is to run a SQL job and retrieve the results from IBM Cloud Object Storage. After calling the SQL Query API, my package simply sets up the required inputs for Cloud Object Storage. The official IBM Cloud Object Storage package does the actual work and retrieves the data. After the data is retrieved, my result-set action in this Sequence returns the CSV file containing the results.

sequences:
sql-job-resultset:
actions: sql-query,cloud-object-storage/object-read,result-set

Using this pattern, I can compose application behavior by referencing the Actions and even Sequences from other packages. I might not even need to write “code” in some cases.

In addition to packages that interact with various services, you can also find templates, which are small solutions consisting of Actions, Triggers, and Sequences that often build upon available packages to solve a problem. Learn more about Templates here.

Functionality

Providing functionality from a package is a balance. Provide too much, and the package becomes more of an app. Provide too little, and developers simply write their own solutions.

To support the use case of obtaining a result from Cloud Object Storage, I found that SQL Query made most of the data I needed available; but, it required just a bit more tweaking to get to the correct Cloud Object Storage input format.

So my package wraps the SQL Query API in just enough code to make it callable as a Function and adds just enough information to make it compatible with Cloud Object Storage. Here you can see the small amount of work I do to add the endpoint, bucket, and key properties using the resultset_location provided by SQL Query. By doing this, I enrich the SQL Query response with information likely needed by other packages—in this case Cloud Object Storage.

const [endpoint, bucket, ...rest] = response.resultset_location.substring(6).split('/');
let key = rest.join('/');

const cos: any = await this.getCosKey(endpoint, this.options.auth, bucket, `${key}/part`);
key = cos.ListBucketResult.Contents[0].Key[0];

return { …response, endpoint, bucket, key };

Ultimately, service offering managers can’t conceive all the ways people will combine them. So little packages such as this provide convenient bridges between them.

My package also provides an API so that services like Cognos Dashboard Embedded can download the result from the web. This may be doing a bit more than is necessary, but I felt the use case was common enough to make it package-worthy.

Customization

A benefit to using Cloud Functions is that if you don’t like them, you can change them. I write most of my Functions code in TypeScript. But the compiler options I was using made the final code hard to read. Changing the config to es2017 helped produce more readable native JavaScript. Now, developers can install my package and then make it their own by adjusting the JavaScript code within the Cloud Functions user interface. Keep this in mind as you create Docker Functions or code that uses other packaging techniques such as NPM or webpack.

With a little bit of thought and code, you can create packages that make IBM Cloud even more approachable to a growing audience of Functions developers.

 

Be the first to hear about news, product updates, and innovation from IBM Cloud