How-tos

Cloud Functions Package Design and Best Practices

Share this post:

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.

IBM Offering Manager

Belinda Vennam

IBM Cloud Developer Advocate

More How-tos stories
December 13, 2018

Java Microservices with MicroProfile – API Documentation

To benefit from the reuse and consistency microservice APIs offer, other developers will need guidance to use your APIs correctly. With annotations defined in the MicroProfile OpenAPI specification from Java EE, it's easy to generate clear documentation.

Continue reading

December 13, 2018

Tutorial: Deploying the Jenkins Helm Community Chart on IBM Cloud Kubernetes Service

The IBM Hybrid Cloud Team has authored a tutorial that will guide you through the steps required to set up and install a Jenkins server and deploy a sample Node and React application on IBM Cloud Kubernetes Service.

Continue reading

December 12, 2018

Deploying to IBM Cloud Private 3.1 with IBM Cloud Developer Tools CLI

IBM Cloud Developer Tools CLI version 2.1.12 adds deployment support for IBM Cloud Private 3.1.

Continue reading