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.
| Constraint | Behavior |
|---|---|
| Synchronous execution | async, await, and Promise are not supported. Context API methods also block and return their results directly. |
| No module loading | import, require, and dynamic third-party package loading are unavailable. |
| No browser APIs | window, document, localStorage, alert, and similar objects are unavailable. |
| No direct network requests | fetch and XMLHttpRequest are unavailable. Invoke a configured API or use runGql. |
| No file-system access | The sandbox cannot access fs, server directories, or local files. |
| No timers | setTimeout, setInterval, and other asynchronous scheduling APIs are unavailable. |
| Dedicated logging | Use 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
| Method | Returns | Description |
|---|---|---|
context.getArg(name) | any | Read an input declared on the node. |
context.setReturn(name, value) | void | Set an output declared on the node. |
context.log(...values) | void | Write a standard runtime log entry. |
context.error(...values) | void | Write 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 }
): anyoperationNamemust match the operation name in the GraphQL document.- Pass
{}when the operation has no variables. - Always pass
{ role: 'admin' }aspermission. - The method returns the GraphQL
dataobject 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>
): anyThe 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>
): numberAlways 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
| Method | Returns | Description |
|---|---|---|
context.getSsoAccountId() | number | null | Return the account ID associated with the current SSO identity, or null when unavailable. |
context.getSsoUserInfo() | string | null | Return 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
Promiseor 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-jsCreate 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.jsPlace 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
- Test normal inputs, empty values, boundary values, and expected failures on the node.
- Inspect execution with
context.log(),context.error(), and runtime logs. - Test the complete Actionflow and verify output types and downstream bindings.
- 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
| Error | What to check |
|---|---|
getArg cannot read a value | Confirm that the input is declared, uses the same name, and has a bound value. |
| A downstream node cannot find an output | Declare the output and use the same name and a compatible type in setReturn. |
async, Promise, or a timer fails | Rewrite the logic synchronously and use Context API methods that return directly. |
window, fetch, or require is undefined | The runtime is not a browser or Node.js environment. Use the platform Context API. |
| A GraphQL field does not exist | Sync the data model, refresh GraphiQL, and use the generated fields and types from this project. |
| A GraphQL operation fails | Check the operation name, variables, { role: 'admin' }, and runtime logs. |
| An API returns no data | Check the code from callThirdPartyApi before reading data. |
| An Actionflow call fails | Use callActionFlow for synchronous Actionflows and createActionFlowTask for asynchronous Actionflows. |
| The node times out | Check loops, GraphQL calls, APIs, and synchronous Actionflows, then confirm the timeout limit for the project plan. |