javascript-actions

Create, call and drop JavaScript actions — client-side logic called from nanoflows, not microflows. Use when behaviour must run in the browser or on the device: DOM, fetch, geolocation, or wrapping a

SKILL.md

GitHub
<!-- skill-index: generated by scripts/skill-index.py; make check-skill-index -->

Find it fast

sed -n '<from>,<to>p' SKILL.md one section rather than reading the file; ./mxcli syntax <topic> answers how do I write this for a fraction of either.

lines section
31-39 When to Use This Skill
40-51 Overview
52-94 Syntax
95-156 Examples
157-168 Calling from a nanoflow
169-193 The generated .js file
194-202 Inspecting
203-211 Validation Checklist
212-221 Common Errors
222-226 Related Documentation
<!-- /skill-index -->

Mendix JavaScript Actions Skill

Guidance for creating, calling, and dropping JavaScript actions in Mendix projects with MDL. JavaScript actions hold custom client-side logic and are called from nanoflows (not microflows — those use Java actions).

When to Use This Skill

  • Adding client-side behaviour (browser/native APIs, DOM, fetch, geolocation, …)
  • Logic that must run on the device rather than the server
  • Calling an existing JavaScript action from a nanoflow
  • Wrapping a marketplace/native capability exposed as a JS action

For server-side custom code, use Java actions instead (see java-actions).

Overview

A JavaScript action is two things on disk:

  1. The model unit (JavaScriptActions$JavaScriptAction).
  2. A source file javascriptsource/<Module>/actions/<Name>.js containing an exported async function.

CREATE JAVASCRIPT ACTION writes both. The MDL body you provide becomes the function body (between the BEGIN USER CODE / END USER CODE markers), so it normally returns a Promise.

Syntax

CREATE [OR MODIFY] JAVASCRIPT ACTION Module.Name ( parameters )
    RETURNS type
    [EXPOSED AS 'caption' IN 'category']
    [PLATFORM Web | Native | Hybrid | All]   -- default Web
AS $$
    <javascript>
$$;

DROP JAVASCRIPT ACTION Module.Name;

Clause order is fixed: returns, then exposed as, then platform, then as.

  • AS $$ ... $$ is mandatory. Omitting the body causes no viable alternative at input '...'. Use a stub: AS $$ return Promise.resolve(false); $$.
  • PLATFORM defaults to Web. Use Native, Hybrid, or All to widen.
  • OR MODIFY updates an existing action in place (UUID preserved); without it, a duplicate name is an error (use create or modify to overwrite).

Parameter types

Same type system as Java actions:

  • Primitives: String, Integer, Long, Decimal, Boolean, DateTime
  • Entity: Module.EntityName · List: List of Module.EntityName
  • Enumeration: ENUM Module.EnumName
  • Type parameter (generics): declare EntityType: ENTITY <pEntity>, then use the bare pEntity for instance parameters
  • Append NOT NULL to mark a parameter required.

Parameter names in the body

The body sees each parameter with its first character lower-cased: A is a, ReadOnly is readOnly, URL is uRL (only the first character changes), _Lead stays _Lead. That is the signature Studio Pro and mxbuild generate, and mxcli writes it. The model keeps the declared name and describe prints it as declared. A body that uses the declared spelling throws ReferenceError at run time and nothing in check or the build reports it.

Examples

Simple action (Web default):

mdl 1;
CREATE JAVASCRIPT ACTION MyFirstModule.JSA_IsOnline () RETURNS Boolean
AS $$
    return Promise.resolve(navigator.onLine);
$$;

Parameters + return:

mdl 1;
CREATE JAVASCRIPT ACTION MyFirstModule.JSA_Add (
    A: Integer NOT NULL,
    B: Integer NOT NULL
) RETURNS Integer
AS $$
    return Promise.resolve(a + b);
$$;

Exposed toolbox action, native platform:

mdl 1;
CREATE JAVASCRIPT ACTION MyFirstModule.JSA_ShowToast (
    Message: String NOT NULL,
    Duration: Integer
) RETURNS Boolean
EXPOSED AS 'Show Toast' IN 'UI'
PLATFORM Native
AS $$
    console.log(message);
    return Promise.resolve(true);
$$;

Idempotent update (UUID preserved):

mdl 1;
CREATE OR MODIFY JAVASCRIPT ACTION MyFirstModule.JSA_Add (
    A: Integer NOT NULL,
    B: Integer NOT NULL,
    C: Integer
) RETURNS Integer
AS $$
    return Promise.resolve(a + b + (c || 0));
$$;

Drop (removes the unit and the .js file):

mdl 1;
DROP JAVASCRIPT ACTION MyFirstModule.JSA_Add;

Calling from a nanoflow

JavaScript actions are invoked with CALL JAVASCRIPT ACTION inside a nanoflow body (arguments use Name = value):

$Sum = call javascript action MyFirstModule.JSA_Add(A = 2, B = 3);

This is the nanoflow counterpart of CALL JAVA ACTION (microflows). Use it in a CREATE NANOFLOW ... BEGIN ... END; body.

The generated .js file

CREATE writes (and OR MODIFY rewrites) javascriptsource/<Module>/actions/<Name>.js:

// This file was generated by Mendix Studio Pro.
// ...
// BEGIN EXTRA CODE
// END EXTRA CODE

/**
 * @param {number} a
 * @param {number} b
 * @returns {Promise.<number>}
 */
export async function JSA_Add(a, b) {
    // BEGIN USER CODE
    return Promise.resolve(a + b);
    // END USER CODE
}

Only the code between the USER CODE / EXTRA CODE markers is preserved when Studio Pro regenerates the file. The MDL $$ body $$ lands inside USER CODE.

Inspecting

LIST JAVASCRIPT ACTIONS [IN Module];
DESCRIBE JAVASCRIPT ACTION Module.Name;   -- re-executable MDL (signature + body)

DESCRIBE output round-trips: it re-parses as a CREATE JAVASCRIPT ACTION.

Validation Checklist

  • Body present: AS $$ ... $$; — never omit it
  • Body returns a Promise (the function is async)
  • Clause order: returns → exposed as → platform → as
  • Required parameters marked NOT NULL
  • Use CALL JAVASCRIPT ACTION from nanoflows, CALL JAVA ACTION from microflows
  • Validate before applying: mxcli check script.mdl

Common Errors

Symptom Cause Fix
no viable alternative at input '...' Missing AS $$ ... $$ body Add a body (stub return Promise.resolve(false);)
already exists — use create or modify to overwrite Duplicate name without OR MODIFY Add OR MODIFY
Action runs nowhere / wrong client PLATFORM too narrow Set PLATFORM All (or the right target)
mismatched input 'platform' Clause out of order Put platform after exposed as, before as
ReferenceError: Url is not defined when the action runs The body uses the declared parameter name; the .js signature has the first character lower-cased Write url, not Url

Related Documentation

  • java-actions — server-side custom code (microflows)
  • write-nanoflows — nanoflow syntax and restrictions
  • mxcli syntax javascript-action — quick syntax reference