javascript-actions · git:20260821.ff81a24 · 2026-08-21 · sha256 c3a059a56cf332b9

javascript-actions git:20260821.ff81a24A

Immutable. This exact content is served forever at /api/v1/blob/c3a059a56cf332b9.

---
name: javascript-actions
description: "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 marketplace JS action."
---

# 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

```sql
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.

## Examples

Simple action (Web default):

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

Parameters + return:

```sql
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:

```sql
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):

```sql
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):

```sql
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`):

```sql
$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`:

```javascript
// 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

```sql
SHOW 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` |

## Related Documentation

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