· mQuark · Technical Guides  · 3 min read

Extending Workflows: Creating Custom User Functions with JavaScript

Learn how to write, sandbox, and debug custom JavaScript logic within your Actionful workflows.

Learn how to write, sandbox, and debug custom JavaScript logic within your Actionful workflows.

While Actionful’s built-in rules cover a vast array of logic, there are times when your business requirements demand the full flexibility of a programming language. Whether it’s complex data transformation, custom algorithms, or specific cryptographic operations, User Functions allow you to inject custom JavaScript code directly into your automated workflows.

The Execution Environment: Security and Speed

In Actionful, User Functions are executed within a Hardened, Isolated Sandbox. This environment is designed with a “Defense-in-Depth” strategy to ensure that your code runs in total isolation from other users and our core system.

To ensure high performance, our environment uses an “Additive Caching” strategy for dependencies. This means common operations remain fast, while your custom logic remains secure.

Writing Your Function

Every User Function is an asynchronous block of code. You do not need to define boilerplate exports; you simply write the logic you need and return a result.

The Global Context

Inside the sandbox, you have access to a specific context object and standard utilities:

  • context.input: This contains the data passed to your function from the workflow (accessible via the GetInput rule).
  • console: A proxied version of the standard console (log, error, warn, info).
  • Standard Utilities: Access to Buffer, setTimeout, setInterval, URL, and more.

Example: Risk Score Calculation

Imagine you receive a list of transaction objects and need to calculate a weighted risk score:

const transactions = context.input.transactions;
const weights = { high: 1.5, medium: 1.0, low: 0.5 };

// Using a custom package (explained below)
const _ = require('lodash');

const totalRisk = _.sumBy(transactions, (tx) => {
  // These logs will appear in the Actionful UI
  console.log(`Processing transaction: ${tx.id}`);
  return tx.amount * (weights[tx.priority] || 1.0);
});

return {
  score: totalRisk,
  timestamp: new Date().toISOString(),
};

Managing Dependencies with NPM

One of Actionful’s most powerful features is the ability to use external libraries. You can manage these directly in your Workspace Configuration.

Adding Packages

To use a library like axios or lodash, you simply add the Package Name and Version to your workspace’s package manifest.

  • Configuration: Define your requirements as a map (e.g., "lodash": "4.17.21").
  • Usage: Once defined in the workspace, these packages are immediately available via the standard require() function in your code.

Debugging and Logs

Actionful makes debugging custom code straightforward by capturing all console output and surfacing it directly in the UI.

  • Log Capping: To maintain performance, we capture up to 1,000 lines of logs per execution.
  • Detailed Errors: If your script fails, the logs are returned alongside the error message, including a stack trace to help you pinpoint the issue.

Safety and Restrictions

To maintain platform stability, the sandbox enforces strict security rules:

  1. Blocked Modules: You cannot access dangerous Node.js built-ins such as child_process, fs (file system), net, or process.exit().
  2. Path Traversal: You cannot use relative or absolute paths (e.g., require('./secret')) to access the host file system.
  3. Timeouts: To prevent runaway logic, every function has a strict execution timeout.

Integrating with Workflows

Once you’ve written your function, you call it using the CallFunction rule within your workflow. Map your workflow data to the Input property, and the engine will handle the rest, ensuring your data is strictly validated against the function’s defined return type.

Start Building in the Sandbox | Explore the Type System

Back to Blog

Related Posts

View All Posts »