This week, I came across an interesting problem when building HTTP APIs on IBM Cloud Functions.
How can Apache OpenWhisk Web Actions, implemented using action sequences, handle application errors that need the sequence to stop processing and a custom HTTP response to be returned?
This came from wanting to add custom HTTP authentication to existing Web Actions. I had decided to enhance existing Web Actions with authentication using action sequences. This would combine a new action for authentication validation with the existing API route handlers.
When the HTTP authentication is valid, the authentication action becomes a ”no-op”, which passes along the HTTP request to the route handler action to process as normal.
But what happens when authentication fails?
The authentication action needs to stop request processing and return a HTTP 401 response immediately.
Does Apache OpenWhisk even support this?
Before explaining how to return custom HTTP responses using web action errors in sequences, let’s review web actions, actions sequences and why developers often use them together…
Incoming HTTP requests are provided as event parameters. HTTP responses are controlled using attributes (
headers) in the action result.
Web Actions can be invoked directly, using the platform API, or connected to API Gateway endpoints.
Here is an example Web Action that returns a static HTML page.
1 2 3 4 5 6 7 8 9
exposing web actions
Web actions can be exported from any existing action by setting an annotation.
This is handled automatically by CLI using the
—web configuration flag when creating or updating actions.
Multiple actions can be composed together into a “meta-action” using sequences.
Sequence configuration defines a series of existing actions to be called sequentially upon invocation. Actions connected in sequences can use different runtimes and even be sequences themselves.
Input events are passed to the first action in the sequence. Action results from each action in the sequence are passed to the next action in the sequence. The response from the last action in the sequence is returned as the action result.
Here is a sequence (
mySequence) composed of three actions (
mySequence will invoke
action_a with the input parameters.
action_b will be invoked with the result from
action_c will be invoked with the result from
action_b. The result returned by
action_c will be returned as the sequence result.
Web Actions from Action Sequences
Using Action Sequences as Web Actions is a useful pattern for externalising common HTTP request and response processing tasks into separate serverless functions.
These common actions can be included in multiple Web Actions, rather than manually duplicating the same boilerplate code in each HTTP route action. This is similar to the ”middleware” pattern used by lots of common web application frameworks.
Web Actions using this approach are easier to test, maintain and allows API handlers to implement core business logic rather than lots of duplicate boilerplate code.
In my application, new authenticated web actions were composed of two actions (
check_auth and the API route handler, e.g.
Here is an outline of the
check_auth function in Node.js.
1 2 3 4 5 6 7 8 9 10 11
check_auth function will inspect the HTTP request and validate the authorisation token. If the token is valid, the function returns the input parameters untouched, which leads the platform the invoke the
route_handler to generate the HTTP response from the API route.
But what happens if the authentication is invalid?
check_auth action needs to return a HTTP 401 response immediately, rather than proceeding to the
handling errors - synchronous results
Sequence actions can stop sequence processing by returning an error. Action errors are indicated by action results which include an “error” property or return rejected promises (for asynchronous results). Upon detecting an error, the platform will return the error result as the sequence action response.
check_auth returns an error upon authentication failures, sequence processing can be halted, but how to control the HTTP response?
Error responses can also control the HTTP response, using the same properties (
body) as a successful invocation result, with one difference: those properties must be the children of the
error property rather than top-level properties.
This example shows the error result needed to generate an immediate HTTP 401 response.
1 2 3 4 5 6
In Node.js, this can be returned using a synchronous result as shown here.
1 2 3 4 5 6 7 8 9 10 11
handling errors - using promises
If a rejected Promise is used to return an error from an asynchronous operation, the promise result needs to contain the HTTP response properties as top-level properties, rather than under an
error parent. This is because the Node.js runtime automatically serialises the promise value to an
error property on the activation result.
1 2 3 4 5 6 7 8 9 10 11
Creating web actions from sequences is a novel way to implement the “HTTP middleware” pattern on serverless platforms. Surrounding route handlers with pre-HTTP request modifier actions for common tasks, allows route handlers to remove boilerplate code and focus on the core business logic.
In my application, I wanted to use this pattern was being used for custom HTTP authentication validation.
When the HTTP request contains the correct credentials, the request is passed along unmodified. When the credentials are invalid, the action needs to stop sequence processing and return a HTTP 401 response.
Working out how to do this wasn’t immediately obvious from the documentation. HTTP response parameters need to included under the
error property for synchronous results. I have now opened a PR to improve the project documentation about this.