The duality between serverless functions and APIs

functions are APIs and APIs are functions

Serverless functions are a fast, convenient, and cost-effective way of developing cloud-native applications. Within minutes, it’s possible to deploy functions to respond to events such as webhooks or REST API requests.

Functions as APIs: Using the shell for IBM Cloud Functions — a new client for working with Apache OpenWhisk and the IBM Cloud — it is convenient to create actions as APIs via web actions. The example below shows a function in Node.js that produces a simple HTML response.

Using the IBM Cloud Functions Shell, deploying a function as an API is a one liner. Adding “.http” suffix on an action name “fn” inside the shell automatically turns the function into a web-accessible endpoint.

It is often the case that several functions must be composed together to create larger serverless applications. For example, a convenient way to secure APIs built out of functions is to compose the business logic (e.g., fn) with an authentication layer. For the latter, I’ll create an auth action to check for the presence of a token with a predefined value. Failure to specify the token will result in an error. When this action is composed into a sequence auth -> fn, the error response will short-circuit the rest of the computation.

A sequence of two functions as an API. The action “fn” is only reachable if a request specifies the expected token (as a query parameter for example ?token=42).

It should be evident from this example that serverless functions can serve as APIs. Functions execute in response to HTTP requests, and the serverless platform automatically provisions resources to execute each of the function invocations. The best of it is that costs are proportional to use. So if a function is idle, the cost is literally nothing: $0.

APIs as functions: Composition of functions is quite powerful. It promotes software reuse and separation of concerns between functions. The authentication function for example could be used with any number of APIs built this way, changing only the backend logic in the sequence.

Now consider a scenario where a serverless function must itself interact with third party services, via RESTful interfaces. Some examples include reading or writing documents to a database, looking up the weather for a zipcode, or using language translation services.

Should one create a serverless function to call out to the API, or should one conflate their function with HTTP requests or library-call-outs instead? The former keeps the functions orthogonal, promotes unit testing, and enables reuse of the API in serverless compositions. The latter pollutes functions which must now generate the HTTP request and handle errors, in addition to the actual business logic that’s intended. This is illustrated in the Node.js snippet below.

// example function to perform an HTTP request...
request(options, function (error, response) {
if (!error) {
// ...then process result, real function goes here
} else {
// ...but must also handle HTTP errors
}
})

Serverless functions, with compositions, encourage an alternate model. HTTP requests can be viewed as generic actions, and furthermore, actions may be automatically generated for third party APIs, especially if they offer a Swagger specification. So a canonical composition with serverless functions becomes one where a first action performs the HTTP request, and a second processes the result. Error handling for a failed request is localized to the action actually interfacing to the external service.

We think this is such a common pattern that we are building a generator for working with third party APIs as actions. In the IBM Cloud Functions shell, we prototyped an operator called |request to <url>| to automatically compile a URL into an action which performs the HTTP request directly. I’ll demonstrate this using the fun translations API. It is quite simple to use: a POST to the service with a JSON body will translate the given text.

Example using |request to <url>| operator to wrap an API into a function. Here, the URL is http://api.funtranslations.com/translate/shakespeare.json.

The shell automatically generates an action to perform the API request. The shell sidecar will indicate that this is a machine generated action for making HTTP requests. The funTranslation action is now a reusable asset. It may be composed with other serverless functions to build more complex applications.

Invoking the action proxies the request to the intended service.

Functions are the building blocks of serverless computing. They lend themselves naturally to compositions for reusable components, libraries, and applications. Functions-as-a-service platforms offer a model where functions run in response to events, including HTTP requests. In this way, functions are APIs. Going further, cloud-native applications will often interface to third party services via APIs. In order to promote composition across APIs in a serverless model, it is also necessary to treat HTTP requests for services as functions. And therefore, APIs are also functions.