Server script
Runs javascript on the server. The script runs as a Node.js AWS Lambda micro-service.
V4 plugin API
This page describes the plugin after the upgrade to plugin API V4, which includes Node.js version 18.
Setting properties
Node script
Will be evaluated as a Node.js script. Can include dynamic data.
Script is Async Function
Allows use of await
at top-level. Expects the script to contain a return statement.
Ignore errors
Wraps the script in a try/catch, and discards errors caught.
Log errors
Log ignored error in the app server logs. (May need a paid plan for logs to show)
Inputs
Data
A text-only parameter, available to the script as data
Keys and values
An arbitrary number of text key-value pairs can be set. Available to the script as properties.keyvalues
, in Bubble's default list form, or in an improved object form as properties.keyvaluesobj
Thing 1
to Thing 3
Parameters of any type. Available to the script as properties.thing1
, etc.
Thing list 1
to Thing list3
Similar to thing1, but in list form. Available to the script as properties.thinglist1
, etc.
Outputs
Return type
usually you will want a "vanilla" type such as text, number, date, yes/no. Can return Bubble data types, API return types.
Returns a list
to produce list of texts, for example.
Multiple Outputs
allows returning multiple data types. The return value of the script needs to be an object with properties matching the output names.
output1 type
to output4 type
the type of output.
outputlist1 type
to outputlist4 type
the type of list output.
Workflow step
The server script is an action, it runs server-side, the same way a database or API action does. It can be run on a page workflow, which can be triggered by a button or other event on the page.
Or it can be run on a backend workflow, which can be triggered by an incoming external API call, or by a database trigger, or by a Schedule API Workflow action.
Script is Async Function
Normally the script is evaluated as a code block, and the last statements value will be used as a return value.
Eval block example
Using promises
fetch('https://api.thecatapi.com/v1/images/search')
.then(response => response.json())
.then(body => body[0].url)
Async function example
Turn on Async Function
allows use of await
at top-level.
Expects the script to contain a return statement.
const response = await fetch('https://api.thecatapi.com/v1/images/search')
const body = await response.json()
const cat_image = body[0].url
return cat_image
Keys and values
Say for example the following key-value pairs are set: "apple": "A spherical fruit" "banana": "A long finger-like fruit"
To access the values in the script, use either of:
desc = properties.keyvalues.find(x => x.key == "apple")?.value
desc = properties.keyvaluesobj["banana"]
Bubble data types as parameters
A parameter set to a Bubble data type appears as an object with functions listProperties(), get(propertyname)
Example with thing1 set to: Search for Fruits:first item
Eval block example
JSON.stringify(properties.thing1.listProperties());
// ['name_text', 'overripe_color_option_color', 'ripe_color_option_color', 'unripe_color_option_color', 'Created By', 'Slug', 'Created Date', 'Modified Date', '_id']
properties.thing1.get('name_text').then(nam => nam);
// 'Banana'
properties.thing1.get('overripe_color_option_color').then(color => color)
// 'Black'
Async function example
return JSON.stringify(properties.thing1.listProperties());
// ['name_text', 'overripe_color_option_color', 'ripe_color_option_color', 'unripe_color_option_color', 'Created By', 'Slug', 'Created Date', 'Modified Date', '_id']
return await properties.thing1.get('name_text');
// 'Banana'
return await properties.thing1.get('overripe_color_option_color');
// 'Black'
A parameter list set to a list of Bubble data types does not appear in javascript as an array, but instead as an iteratable object.
Example with thinglist1 set to: Search for Fruits
Eval block example
properties.thinglist1
.length()
.then(len => properties.thinglist1.get(0, len))
.then(arr => Promise.all(arr.map((item) => item.get("name_text"))))
.then(arrtexts => arrtexts); // array of names
Async function example
// var len = await properties.thinglist1.length();
var result = [];
for await (const item of properties.thinglist1) {
// result.push(item.listProperties());
result.push(await item.get("name_text"));
}
return result; // array of names
An extended example of reading properties of nested objects: showing book titles and their associated category names
Example with thinglist1 set to: Search for Books
var len = await properties.thinglist1.length();
var result = [];
for await (const item of properties.thinglist1) {
// result.push(item.listProperties());
result.push(await item.get("title_text"));
var cat = await item.get("category_custom_category");
// result.push(cat.listProperties());
var catname = await cat.get("name_text");
result.push(catname);
}
return result; // list of text
Some examples are in this app editor
Vanilla data types as parameters
A parameter set to a vanilla data type (text, number, etc)
Example with thing1 set to: Search for Fruits:count
Eval block example
var result = properties.thing1;
Async function example
var result = properties.thing1;
return result;
A parameter list set to a list of vanilla data types
Example with thinglist1 set to: Search for Fruits:each items name
Eval block example
properties.thinglist1
.length()
.then(len => properties.thinglist1.get(0, len))
.then(arr => Promise.all(arr.map(item => item)))
.then(arrtexts => arrtexts) // this line to show where to add more processing
Async func example
// var len = properties.thinglist1.length();
var result = [];
for await (const item of properties.thinglist1) {
result.push(item);
}
return result;
Ignore error
Wraps the script in a try/catch, and ignores or logs the errors.
Multiple outputs
Enabling this allows returning multiple data types. For example, set output1 type
to text and output2 type
to number:
return {output1: "apple", output2: 69};
Timing
Bubble then spins up an AWS Lambda (micro-function) instance to run the Node.js script. Typical start timings are:
- 4 - 8 seconds
- 1 - 2 seconds, if a server script has been run recently and can be reused.
The results of the Node.js script are made available on the workflow step to be used in other workflow steps, such as writing to database.
Limitations
The script has no access to the Bubble app's database or other workflow step results etc. It only knows the data it is passed as parameters, and can only return results through the workflow step. It does have internet access, so could be made to access the app via API calls, but that is an even more indirect approach.
Toolbox does not know in advance which npm package you may want to use, so it does not load any. The packages that come built-in to Node are available.
There is a script size limit of 2 million chars.
Server script is a plugin server action, which runs the code on AWS Lambda, with the default configuration of 30 seconds. If exceeded, there is an error thrown and the workflow is terminated.
Workarounds for the 30 second limit:
- Move any long-running API call to the API Connector, pass results into Server script as text.
- Restructure the code so each script can complete under 30 seconds
- Use API connector to run your own configured AWS Lambda
- Other third party server to send results to the bubble app via webhook
Security
As server script evaluates an arbitrary Node.j script, there is the potential for a Bubble app to run a script entered by an untrusted end user. Be cautious, as you the Bubble app owner are responsible for any action the script takes (such as API calls to anywhere).
A recommended approach is to send user-entered data into the script via one of the supplied parameters. Within the script ensure the data is escaped appropriately if using it in, for example, your own eval function.