Skip to content

JS actions

Use JavaScript to orchestrate several actions in one run, inside a restricted sandbox, with a whitelist of callable actions and cycle detection at publish time.

4 min read

A JS action is an action written in JavaScript. A JS action does not connect to any database. It calls other actions with executeAction(slug, params), combines their results and returns them. Use a JS action when a task has several steps, for example fetching data from two actions and joining it, or running a series of write actions in order.

  • Editor and above: create, edit and publish JS actions.
  • Admin: allow a JS action to call a restricted action (the whitelist, described below).
  1. Go to Build > Actions and click New action.
  2. Enter a Name and Slug, set Kind to JavaScript (no data source is needed) and select the Mode. Select Writes data if the JS action calls write actions, so that users confirm before running it.
  3. Click Create, then write the code in the Definition tab. Type executeAction(" and the editor suggests the actions you can call.
  4. Declare the JS action’s own params in the Params section, as for a SQL action. The code reads them from ctx.params.
  5. Click Save draft, then click Publish.

The code must export default a function that takes ctx and returns a result:

export default (ctx) => {
const orders = executeAction('orders-by-status', { status: ctx.params.status });
const customers = executeAction('list-customers');
const emailById = {};
customers.forEach((c) => {
emailById[c.id] = c.email;
});
return orders.map((o) => ({
id: o.id,
total: o.total,
customer_email: emailById[o.customer_id] || null,
}));
};
  • ctx contains currentUser (the id and email of the user running it), environment (id, slug, isProduction), params (the JS action’s own params) and vars (the environment’s variables).
  • executeAction(slug, params) returns the array of rows from the called action. If the child action fails, executeAction throws an error, which you can catch with try/catch.
  • If the function returns an array of objects, the Run action page displays a table. If it returns a single value, the page displays one line.
  • Two calls to the same action with the same params in one run execute only once and share the result.

JS actions run in a server-side QuickJS sandbox:

  • There is no fetch, require, file system or network access. executeAction is the only way to reach external data.
  • A run can make at most 50 executeAction calls.
  • JS actions that call JS actions can nest at most 3 levels deep.
  • Each run is limited to 30 seconds and 128 MB of memory.
  • Each child action commits on its own. No transaction spans the whole chain: if a later step fails, data written by earlier steps remains. Use try/catch to call a compensating action when needed.
  • JS actions cannot run in the background yet: they cannot be queued, bulk-run or scheduled.

Each executeAction call goes through the full permission checks of the child action, using the permissions of the user running the JS action. Publishing a JS action grants no additional permissions: if the user running it is not allowed to run a child action, that call is refused.

Whitelist: allow a JS action to call a restricted action

Section titled “Whitelist: allow a JS action to call a restricted action”

A common setup is to keep the child action restricted and allow only the JS action to call it. Users can then use the child action only through the JS action.

  1. Open the child action, then the Settings & access tab and the Action permissions card (Admin role required).
  2. Switch Permission mode to restricted — only those granted and confirm.
  3. Under Grant run access to, select the JS action type, select the JS action in Pick a JS action…, then click Grant.

Action permissions card in restricted mode, with a JS action in the grant listAction permissions card in restricted mode, with a JS action in the grant list

A grant to a JS action takes effect only while the child action is restricted. The JS action can then call the child action with the permissions of the user running the JS action, and users do not see the child action on the Run action page unless they are granted access separately.

Editors still see every action in the editor’s suggestions, including restricted actions. An action that appears in the suggestions is not necessarily callable: the call succeeds only after an admin has added the JS action to that action’s grant list.

  • The slug in executeAction must be a fixed string, for example executeAction('list-customers'). A slug built at run time (executeAction(name)) is refused, because publishing builds the dependency graph from the code itself.
  • Every called action must exist in the project.
  • Cycles are not allowed: if A calls B and B calls A (directly or indirectly), publishing is refused and the page shows the path, for example a → b → a.

After publishing, an action that a JS action calls cannot be deleted until that call is removed from the JS action.