What’s new web actions?

A little over a month ago, I introduced web actions for creating serverless HTTP handlers with OpenWhisk.

Web actions allow one to run code in response to HTTP events, without deploying or managing their own servers. Web actions are built into the OpenWhisk core, and hence require no extra tooling or configuration (e.g., no need for an “API gateway” for the common case). This provides a much more convenient way to build front-end or back-end logic (e.g., user interface or REST API).

Web actions were first featured as an experimental API, and thanks to user feedback from our growing Slack community, we made several enhancements and graduated web actions to the core API. I will review the new features with a brief example.


Here is a Node.js function which takes a given msg argument and uses it as a response in the HTTP body:

function main(args) {
return { body: args.msg }
}

Save this function to a file called msg.js to turn it into a web action, using a new wsk CLI flag --web:

$ wsk action create hi msg.js --web yes

The action is now accessible from the new web API path:

$ curl https://{APIHOST}/api/v1/web/{ACTION NAME}

For IBM Cloud Functions, the APIHOST is openwhisk.ng.bluemix.net. The “action name” is the fully qualified name of the action which must include the namespace, the package name (or default if the action is not in a named package), and the name of the action. For convenience you can retrieve the full URL with the CLI:

$ wsk action get hi --url
https://openwhisk.ng.bluemix.net/api/v1/web/rabbah_demos/default/hi

You can plug the resultant URL into your browser, add a query parameter, and see the response. Here’s mine as an example:

https://openwhisk.ng.bluemix.net/api/v1/web/rabbah_demos/default/hi?msg=hello medium reader

The result of a web action, like any other OpenWhisk action must be a JSON object (e.g., { body: "hello." }). However web actions impose a schema on the response. It must include a body property which contains the HTTP response content. Optionally, response headers and a statusCode may be included. When the former is omitted, the content-type is automatically inferred. The status code is 200 unless specified explicitly.

Web actions receive query parameters as first class parameters in the action; for instance, the action directly accesses args.msg in the example above. This also applies to HTTP body entities for content-type application/json or application/x-www-form-urlencoded. If a query parameter also appears in the body, the latter supersedes.


It is also possible to access the “raw” HTTP query string and body entity. This means web actions can receive content-types other than JSON objects. A raw web action receives the request query parameters and body via two properties of the input argument: __ow_query and __ow_body. The former is a string as in __ow_query = "msg=Hello&name=Reader", and the latter is either a plain string, or a base64 encoded string when the content-type representation is binary data. An example of a raw web action that receives a JSON object (which is considered a binary data type) as __ow_body and converts it to String follows:

function main(args) {
let json = new Buffer(args.__ow_body, 'base64') # base64 decode
return { body: json.toString('utf-8') } # json as string
}

A raw web action must be declared as such when created or updated. Say the example function above is saved in a file called raw.js:

$ wsk action update hi raw.js --web raw
$ curl https://openwhisk.ng.bluemix.net/api/v1/web/rabbah_demos/default/hi -H "Content-Type: application/json" -X POST -d '{"msg":"Hello"}'
{"msg":"Hello"}

Note that the response content-type is “text” not JSON; you can confirm this by inspecting the headers (use curl -v), or by using one of several web action-name extensions that automatically set the content-type for the response based on the value of the extension. Specifically, repeating the HTTP request with a .text suffix on the action name as in hi.text will automatically set the response header Content-Type: text/plain; charset=UTF-8. The API provides 5 common extensions: .html.http,
.json.text and .svg, with .http as the default if no extension is specified.

All but the .http extension allow you to project a specific field from the action response. So for the raw.js example, the value to project as “text” is body. Here is the actual curl command (with the extension and the projected value in bold font):

$ curl https://openwhisk.ng.bluemix.net/api/v1/web/rabbah_demos/default/hi.text/body -H "Content-Type: application/json" -X POST -d '{"msg":"Hello"}'

The extensions are convenient for returning common formats, allowing you to eschew setting headers explicitly. The default projection for .text is /text (e.g., return { text: “some string” }) and may be omitted. For .html it is /html and for .svg it is /svg. There is no default projection for .json, and the .http does not support this feature.


So in summary:

  1. Web actions may be created using the wsk CLI via --web yes to enable, --web no to disable, and --web raw for raw web actions.
  2. Raw web actions receive __ow_body and __ow_query as uninterpreted entities. In addition, all web actions receive headers, unmatched paths, and the HTTP methods respectively as __ow_headers, __ow_path, __ow_method.
  3. The .http extension may be omitted as it is the default. For added convenience, a new .svg extension now exists for serving image/svg+xml content.
  4. Lastly, we added support for all the HTTP methods, so web actions allow you to handle PATCH, HEAD, and OPTIONS methods as well as the common REST verbs POST, PUT, GET, and DELETE.

More information on web actions is available on GitHub. The features described in this article are available to try with IBM Cloud Functions.

Do you have feedback for us? Join us on Slack and be part of the evolution of OpenWhisk.