Skip to Content
DocumentationDevelopersDevelop with Run Code

Develop with Run Code

Run code is the backend code extension node in an Actionflow. It runs JavaScript in a restricted environment, allowing developers to process data, execute GraphQL, invoke APIs and Actionflows, and return values to downstream nodes.

Use Run code when existing nodes cannot express the required logic directly. Prefer dedicated database, API, and Actionflow nodes when they can perform the operation, because their configuration and runtime behavior are easier to maintain and troubleshoot.

Development workflow

Add the node

Add Run code to an Actionflow. Select the node and write JavaScript in the editor on the right. Expand the editor when you need more space.

Declare inputs

Add every value required by the code to the node’s input configuration. Bind each input to an Actionflow input, an earlier node result, or another data source.

Read an input by name with context.getArg():

const quantity = context.getArg('quantity'); const unitPrice = context.getArg('unitPrice');

The name must exactly match the input declared on the node.

Declare outputs

Add every value needed by downstream nodes to the output configuration and select its type. Set the value in code with context.setReturn():

const total = quantity * unitPrice; context.setReturn('total', total);

The output name and value type must match the node configuration. Undeclared return values are not available to downstream nodes.

Test the node

Provide test values for the inputs, run the node, and inspect its outputs and runtime logs. After the node works in isolation, test the complete Actionflow with its downstream nodes.

Minimal complete example

This example reads an order quantity and unit price, validates the input, and returns the total:

const quantity = context.getArg('quantity'); const unitPrice = context.getArg('unitPrice'); if (typeof quantity !== 'number' || quantity <= 0) { context.error('quantity must be greater than 0'); throw new Error('Invalid quantity'); } const total = quantity * unitPrice; context.log('Calculated total:', total); context.setReturn('total', total);

Configure two number inputs, quantity and unitPrice, and one number output, total.

Runtime environment

Momen runs the code synchronously in a backend JavaScript sandbox. It is neither a browser environment nor a complete Node.js runtime.

ConstraintBehavior
Synchronous executionasync, await, and Promise are not supported. Context API methods also block and return their results directly.
No module loadingimport, require, and dynamic third-party package loading are unavailable.
No browser APIswindow, document, localStorage, alert, and similar objects are unavailable.
No direct network requestsfetch and XMLHttpRequest are unavailable. Invoke a configured API or use runGql.
No file-system accessThe sandbox cannot access fs, server directories, or local files.
No timerssetTimeout, setInterval, and other asynchronous scheduling APIs are unavailable.
Dedicated loggingUse context.log() and context.error() instead of relying on console.log().

Node duration is governed by the Actionflow execution mode, project plan, and runtime lifecycle. See Build Actionflows — Timeout policy.

Context API

The global context object provides data I/O, logging, and backend service access. The Code tools list in the editor shows the methods available in the current environment.

Inputs, outputs, and logs

MethodReturnsDescription
context.getArg(name)anyRead an input declared on the node.
context.setReturn(name, value)voidSet an output declared on the node.
context.log(...values)voidWrite a standard runtime log entry.
context.error(...values)voidWrite an error-level runtime log entry.

context.error() records a log entry but does not stop execution. Throw an error explicitly when the node should fail.

Data and service calls

runGql

Execute a GraphQL query or mutation from the project’s Runtime API:

context.runGql( operationName: string, gql: string, variables: Record<string, any>, permission: { role: string } ): any
  • operationName must match the operation name in the GraphQL document.
  • Pass {} when the operation has no variables.
  • Always pass { role: 'admin' } as permission.
  • The method returns the GraphQL data object and throws when execution fails.

Open Developer access → GraphiQL in the Momen editor to inspect the current project schema and test an operation. After changing the data model, sync or publish the backend changes and refresh the schema.

const orderId = context.getArg('orderId'); const gql = `query GetOrder($id: bigint!) { order_by_pk(id: $id) { id status total } }`; const result = context.runGql( 'GetOrder', gql, { id: orderId }, { role: 'admin' } ); if (!result.order_by_pk) { throw new Error('Order not found'); } context.setReturn('order', result.order_by_pk);

See the Runtime API Reference for generated database fields and GraphQL conventions.

callThirdPartyApi

Invoke a third-party API already configured in the project:

context.callThirdPartyApi( operationId: string, args: Record<string, any> ): { code: number; data: any }

The operation ID and argument names must match the API configuration. Check the HTTP status code before reading the response data:

const response = context.callThirdPartyApi('get_shipping_quote', { fz_body: { postcode: context.getArg('postcode'), }, }); if (response.code < 200 || response.code >= 300) { context.error('API request failed:', JSON.stringify(response)); throw new Error('Shipping API request failed'); } context.setReturn('price', response.data.price);

callActionFlow

Invoke a synchronous Actionflow and wait for it to complete:

context.callActionFlow( actionFlowId: string, versionId: number | null, args: Record<string, any> ): any

The method returns the outputs declared by the target Actionflow. The second argument is retained for backward compatibility; always pass null instead of selecting an Actionflow version.

const result = context.callActionFlow( '8a76458b-3572-48c3-ae54-b478286eac61', null, { orderId: context.getArg('orderId') } ); context.setReturn('status', result.status);

Use callActionFlow only with synchronous Actionflows. Use createActionFlowTask for an asynchronous Actionflow.

createActionFlowTask

Create an asynchronous Actionflow task:

context.createActionFlowTask( actionFlowId: string, versionId: number | null, args: Record<string, any> ): number

Always pass null as the second argument. The method returns the task ID immediately and does not return the final Actionflow output.

const taskId = context.createActionFlowTask( 'd3ea4f95-5d34-46e1-b940-91c4028caff5', null, { reportId: context.getArg('reportId') } ); context.setReturn('taskId', taskId);

uploadMedia

Download an asset from an accessible URL and save it to Momen media storage:

context.uploadMedia( url: string, headers: Record<string, string> ): { mediaType: string; id: number }

Pass {} when the source URL does not require request headers. Use the returned id in an image, video, or file field, or pass it to a downstream node.

const media = context.uploadMedia( context.getArg('fileUrl'), {} ); context.setReturn('mediaId', media.id); context.setReturn('mediaType', media.mediaType);

User and platform context

SSO user information

MethodReturnsDescription
context.getSsoAccountId()number | nullReturn the account ID associated with the current SSO identity, or null when unavailable.
context.getSsoUserInfo()string | nullReturn the SSO provider’s user profile as a JSON string, or null when unavailable.

Check for an empty value before parsing the user profile:

const rawUserInfo = context.getSsoUserInfo(); const userInfo = rawUserInfo ? JSON.parse(rawUserInfo) : null;

Use third-party libraries

The runtime cannot execute import or require directly, but you can bundle a compatible library into a self-contained script locally and place the generated code before the node’s business logic.

A suitable library must meet all of these requirements:

  • It runs synchronously and does not require a Promise or asynchronous initialization.
  • It does not depend on Node.js built-ins, the file system, or native extensions.
  • It does not require window, document, workers, or other browser APIs.
  • It can be bundled into one JavaScript file of a manageable size.

Bundle a self-contained script

This example exposes crypto-js as a global variable with Webpack. Create a separate directory and install the build dependencies:

mkdir run-code-library cd run-code-library npm init --yes npm install --save-dev webpack webpack-cli npm install crypto-js

Create entry.js:

const CryptoJS = require('crypto-js'); module.exports = CryptoJS;

Create webpack.config.js:

const path = require('path'); module.exports = { mode: 'production', entry: './entry.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'crypto-js.bundle.js', library: { name: 'CryptoJS', type: 'var', }, }, };

Build the bundle:

npx webpack --config webpack.config.js

Place the contents of dist/crypto-js.bundle.js at the beginning of the node, followed by the code that uses the generated global variable:

// Contents of crypto-js.bundle.js // ... const content = context.getArg('content'); const digest = CryptoJS.SHA256(content).toString(); context.setReturn('digest', digest);
⚠️

The library remains subject to the sandbox and node timeout. Pin and record dependency versions, review licenses and source provenance, and rebuild and retest after upgrades. Never use an untrusted bundle or place secrets in library code or logs.

Test and deploy

  1. Test normal inputs, empty values, boundary values, and expected failures on the node.
  2. Inspect execution with context.log(), context.error(), and runtime logs.
  3. Test the complete Actionflow and verify output types and downstream bindings.
  4. Save the Actionflow, then deploy the backend change by syncing changes or publishing.

Both syncing and publishing update the Actionflow. After deployment, every client that invokes the Actionflow uses the new code.

⚠️

Never write passwords, tokens, secrets, complete user profiles, or other sensitive values to runtime logs.

Common errors

ErrorWhat to check
getArg cannot read a valueConfirm that the input is declared, uses the same name, and has a bound value.
A downstream node cannot find an outputDeclare the output and use the same name and a compatible type in setReturn.
async, Promise, or a timer failsRewrite the logic synchronously and use Context API methods that return directly.
window, fetch, or require is undefinedThe runtime is not a browser or Node.js environment. Use the platform Context API.
A GraphQL field does not existSync the data model, refresh GraphiQL, and use the generated fields and types from this project.
A GraphQL operation failsCheck the operation name, variables, { role: 'admin' }, and runtime logs.
An API returns no dataCheck the code from callThirdPartyApi before reading data.
An Actionflow call failsUse callActionFlow for synchronous Actionflows and createActionFlowTask for asynchronous Actionflows.
The node times outCheck loops, GraphQL calls, APIs, and synchronous Actionflows, then confirm the timeout limit for the project plan.
Last updated on