Quickstart
Five minutes to know which language to pick, where you can write code, and the handful of runtime objects you'll see in every script.
Choose JavaScript or Python
Both are supported as inline scripting languages, but they have very different ceilings:
| Aspect | JavaScript (Nashorn) | Python (GraalPy) |
|---|---|---|
| Engine | Nashorn: JS engine in the JVM | GraalPy: Python 3.13 inside the JVM |
| Available everywhere | Yes (default) | Yes |
| Java class access | Native: Java.type("…"), importPackage() | import java, then java.type("java.util.ArrayList") |
| Returning a value | Assign to value, or last expression | Last expression, or scriptResult.setResult(value) |
| Modern syntax | Standard JavaScript | Python 3.13: f-strings, print(), modern exception handling |
| Third-party packages | Load JS libraries via load("…") | No pip and no bundled pandas / numpy / boto3. The standard library is included. |
| Best for | All inline logic, Java-API-heavy work | Inline logic when Python syntax is preferred |
Embedded Python is already Python 3.13, so you do not need an external interpreter just for the language version. You do need one for packages: use the Execute Script Local or Remote via SSH flow type, which runs your own interpreter with pip, virtual environments and packages such as pandas, numpy or boto3. Choose by workload, not by Python version.
Default recommendation: JavaScript. The rest of this guide and most of the recipes use JS unless Python adds something specific.
Where you can write code
Every place Etlworks shows a scripting text area, you can drop in JavaScript or Python. Common ones:
| Location | Typical use |
|---|---|
| Mapping → field function | Calculate a destination field's value from source fields, or anonymize. |
| MAPPING → Additional Transformations → Filter | Reject rows by expression. Returns true (keep) or false (drop). |
| MAPPING → Additional Transformations → Validate | Reject rows or halt the flow with explicit TaskResult codes. |
| Before Prepare Source Query / Before Extract / For Each Row / After Extract / After Load | Five scripting transformations that run at different points of the source-to-destination pipeline. |
| Format → Preprocessor | Modify the raw source message before parsing: fix encoding, fill empty bodies, store the original. |
| Nested flow → Condition / Loop condition | Decide whether the next step runs, or how many iterations a loop performs. |
| Execute before / Execute on Exception | Setup or cleanup hooks attached to a transformation. |
| Action Conditions (database destinations) | Pick INSERT / UPDATE / DELETE / MERGE / nothing per row. |
| Execute JavaScript or Python flow | A dedicated flow type that runs only your code, no source/destination involved. |
| Dynamic SQL, function parameters, advanced config fields | Anywhere you see "Open in Editor." |
Open the editor
Wherever you see a scripting text area:
- Click Open in Editor.
- Confirm JavaScript is selected (or change to Python).
- Write your script. The editor has syntax highlighting, indentation, and a full-screen mode.
Your first script
The simplest case: a calculated field in Mapping. Click the pen icon next to a destination field, choose Open in Editor, write:
var first = dataSet.getFieldValue(currentRow, 'first_name');
var last = dataSet.getFieldValue(currentRow, 'last_name');
value = first + ' ' + last;
The same thing in Python:
first = dataSet.getFieldValue(currentRow, "first_name")
last = dataSet.getFieldValue(currentRow, "last_name")
f"{first} {last}"
Both run for every row of the source dataset and write the result to the destination field. dataSet and currentRow are injected by name, no import needed.
Returning a value
How you return a result depends on the language:
| Language | How to return |
|---|---|
| JavaScript | Assign to value, OR rely on the last evaluated expression. |
| Python | The last evaluated expression, OR scriptResult.setResult(the_value). A non-null expression wins; a trailing assignment is not an expression. |
// Both work:
value = first + ' ' + last;
// or — last expression becomes the result automatically
first + ' ' + last;
For boolean fields (Filter, Conditions, Loop conditions), return true or false. For Validate, return one of the TaskResult values (see Validate).
Runtime objects
A handful of objects are injected by name into your script. Which ones depend on where you're writing code:
| Object | What it is | Available in |
|---|---|---|
etlConfig | Current flow's runtime config + key/value storage | Mapping, scripting transformations, Script flow, almost everywhere |
scenario | The flow's scenario: variables, source/destination registry | Same as etlConfig |
dataSet | The current source dataset | Mapping, For Each Row, After Extract, Filter, Validate, Action Conditions |
currentRow | The current DataSetRecord being processed | For Each Row, Mapping field functions, Filter, Validate |
row | 0-based row index (integer) | For Each Row, Filter, Validate |
fieldValue | The field value being calculated (Mapping field functions only) | Mapping field functions |
message | The raw source message (Format Preprocessor only) | Format → Preprocessor |
filter | The CSV row filter object | CSV Format → row filter |
destination | The destination context | Action Conditions |
scriptResult | Where Python writes its return value | Python scripts only |
Full availability map per location: Reference → Runtime objects.
Logging
Two ways to log from a script:
// Add an info line to the flow log
etlConfig.log("processed " + dataSet.getRecordCount() + " rows");
// Or use the JVM logger directly
com.toolsverse.util.log.Logger.log(
com.toolsverse.util.log.Logger.SEVERE,
null,
"Something went wrong"
);
Lines logged via etlConfig.log(...) appear in the flow's run log alongside platform-emitted entries: searchable from the UI and via tail-flow-log in the CLI.
From here: