How To Use Serverless Functions

In the article, we have explained the high-level basics of the Serverless Functions that allow to extend Rossum functionality without the setup and maintenance of additional services.
In this article, we will explain the Functions in more detail, lead you through the main configuration steps, and show sample code snippets of Functions.

At the end, you should be able to understand how the following sample functions work:

How The Functions Work

Each Function has to have a Hook object of function type. This Hook object is assigned to your Rossum queue and is run on various events happening with an annotation. Every time the triggering event occurs, the Function will be run. One Function can be triggered on multiple events, and you can also have more Function hooks attached to one Queue.

A Function can be triggered by one of the following actions:

  • Annotation status changed
  • Annotation content was extracted for the first time
  • User updated the annotation data
  • Annotation is being moved to exported state

Functions vs. Webhooks

Functions and Webhooks share the same basic setup with regards to API communication. Still, there are a few differences:

FunctionsWebhooks
Don’t need server to be run on as they are executed within the Rossum platformNeed to be run on your own remote server
Must be written in JavaScript and be able to run within NodeJS 12 runtime or in Python.Code can be written in any programming language
Cannot cache data in a databaseCustom database can be setup on the server for caching relevant data and speeding up the data queries. Useful for vendor and product matching.

In general, serverless functions are a good option if you want to:

  • run a code without having your own server
  • implement a functionality that can be implemented using JavaScript
  • implement simple data updates on a document

The Functions are especially useful if you need to do data validation, checksums, cross-checks, or apply any other custom rules to the extracted data to notify the user about the incorrect input and prevent exporting incorrect data.

📘

Access to internet

Serverless Functions can be configured to access internet. However such option is currently disabled by default for every function. Please let us know about your intentions at [email protected] if you would like to have the access enabled and read more about the access to internet.

The Core Functions Part

You can configure your Rossum Queue to use a Function as described in our previous article. But how should the code of the Function look like from a developer’s perspective?

The most important part of every Function is the Hook request handler. The request handler is the main and mandatory function that accepts input from Rossum, runs the main logic, and produces output of the Function hook. It must be called as follows:

exports.rossum_hook_request_handler()

You can find the examples of the input request and output response format here. The request handler accepts the annotation content or annotation status data, depending on what action triggers the Function hook.

Within the request handler method, you can create any logic you would like to be applied to an annotation. In the Function code you will see in the Rossum UI by default, you can find the pre-defined logic that will display a warning in the user interface when any of the values in the Total base column is greater than 1,000,000.

As the Functions conduct very similar actions, we have prepared a couple of helper functions that you can use in your code. For example, if you want to iterate over the annotation content, you can use findBySchemaId() function to search for datapoints with particular schema_ids:

const findBySchemaId = (content, schemaId) =>
  content.reduce(
    (results, dp) =>
    dp.schema_id === schemaId ?
    [...results, dp] :
    dp.children ?
    [...results, ...findBySchemaId(dp.children, schemaId)] :
    results,
    [],
  );

Which will return one or multiple datapoints of the annotation data.

If you would like to show an error or warning to the user in the UI, you can use createMessage() function:

const createMessage = (type, content, datapointId = null) => ({
  content: content,
  type: type,
  id: datapointId,
});

Which can produce an error, information, or a warning message:

390

If you want to update a datapoint with a new value, you can use the createReplaceOperation():

const createReplaceOperation = (datapoint, newValue) => ({
  op: 'replace',
  id: datapoint.id,
  value: {
    content: {
      value: newValue,
    },
  },
});

This function can overwrite the original value with a new one (or simply add a new value if there wasn’t any before).

For more technical information, see our API documentation.

Testing Custom Function

If you are struggling with testing the functions, follow this article.