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
February 13, 2019

Simplify and Automate Deployments Using GitOps with IBM Multicloud Manager 3.1.2

Use Argo CD, a GitOps continuous delivery tool for Kubernetes, and IBM Multicloud Manager to achieve declarative and automated deployment of applications to multiple Kubernetes clusters.

Continue reading

February 11, 2019

Solving Business Problems with Splunk on IBM Cloud Kubernetes Service

In this tutorial, we will install Splunk Connect for Kubernetes into an existing Splunk instance. Splunk Connect for Kubernetes provides a way to import and search your Kubernetes logging, object, and metrics data in Splunk.

Continue reading

February 8, 2019

A How-To for Migrating Redis to IBM Cloud Databases for Redis

If you’re moving your data over to IBM Cloud Databases for Redis, you’ll need to take some steps to successfully migrate all of your data. We’ve got you covered. In this post, we’ll show you a quick way to start migrating your data across to Databases for Redis, whether your database is on-premise or in the cloud.

Continue reading