Skip to content
Back to skills

Write Nanoflows

ASecurity

Nanoflow syntax in MDL — shared with microflows, but client-side and restricted. Use before writing any CREATE NANOFLOW, when a nanoflow validation error needs explaining, or when deciding between a nanoflow and a microflow.

  • 128 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added September 26, 2026
ai-agentsjavascriptgojavasqlnodedebugginggitapidatabasesecurity

Works with

  • cli
  • api

Security analysis

A100/100

Scanned October 4, 2026

npx -y skills add mendixlabs/mxcli --skill write-nanoflows --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Write Nanoflows?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Write Nanoflows
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/mendixlabs-write-nanoflows/badge)](https://www.skillsdirectory.com/skills/mendixlabs-write-nanoflows)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: write-nanoflows
description: "Nanoflow syntax in MDL — shared with microflows, but client-side and restricted. Use before writing any CREATE NANOFLOW, when a nanoflow validation error needs explaining, or when deciding between a nanoflow and a microflow."
---

# Mendix Nanoflow Skill

This skill provides guidance for writing Mendix nanoflows in MDL syntax. Nanoflows share syntax with microflows but execute client-side with restricted capabilities.

## When to Use This Skill

Use this skill when:
- Writing CREATE NANOFLOW statements
- Debugging nanoflow validation errors
- Understanding nanoflow restrictions vs microflows
- Building mobile or offline-capable features

If you're not sure whether the logic belongs in a nanoflow or a microflow, read the next section first. The mirror lives in [write-microflows](../write-microflows/SKILL.md) — keep both copies in sync.

## Changing an Existing Nanoflow

Choose the mode by who owns the nanoflow ([choose-edit-mode](../choose-edit-mode/SKILL.md)):

- **Created by your MDL scripts, and not edited in Studio Pro since:** edit the script
  (or fresh `describe` output) and re-run `create or modify`.
- **Authored in Studio Pro:** prefer `alter nanoflow X { insert/replace/drop … }`
  (targets by output variable, caption or statement pattern). `create or modify` of `describe`
  output also works as a patch: an unchanged definition writes nothing, and an inserted,
  replaced or dropped statement (at the top level or in an `if` branch) is spliced in,
  leaving every other node, merge and curve as stored — a guard clause (`if … then return;
  end if;`) too, its return a new end event, and so is the `return` ending a new activity's
  error handler; a changed `return` value or `if` condition is set on
  the stored end event or decision, and a body without a trailing `return` means the stored end. The
  header (documentation, return type, parameters added, retyped or — when unused —
  removed) is set on the stored document, and a stated `@position` or `@start` moves the
  stored node, keeping its flows. A change it cannot patch — anything inside a loop body
  or error handler, a redrawn `@anchor`/`@curve`, any other `return` added or taken away —
  rebuilds the whole nanoflow under mdl 0
  (warning `MDL-V1-REBUILD`: element IDs renumbered, merges removed, curves reset) and
  is refused under `mdl 1;`.
- **Rebuilding deliberately: `drop nanoflow X;` and `create nanoflow X …` in ONE script.**
  The drop's module-role grants (and the unit's ID and folder) carry only to a create later in
  the same script or REPL session; a drop in one run and a create in the next loses every
  `grant execute`. `drop` prints the roles it removed and the `grant` to restore them.
- **Changing something inside a loop body** (either owner)
: neither `create or modify`
  (under `mdl 1;`) nor an `alter` aimed at an activity in the loop can make it — `alter`
  does not splice inside a loop. Replace the **whole loop**, addressed by its handle, with
  the body as it should be (`replace loop $Item in $Items with begin loop $Item in $Items
  begin … end loop; end;`). Everything outside the loop keeps its `$ID`s, positions and
  curves; the loop and its body are rebuilt. For a nested loop, replace the outer one.

## When to Use a Nanoflow vs a Microflow

| Scenario | Use |
|----------|-----|
| Client-side form validation before save | Nanoflow |
| UI navigation and page routing | Nanoflow |
| Calling device features (GPS, phone, camera) | Nanoflow |
| Offline data access and local storage | Nanoflow |
| Calling JavaScript actions (NanoflowCommons) | Nanoflow |
| Showing progress indicators / confirmation dialogs | Nanoflow |
| Querying the database | Microflow |
| Calling REST services or external actions | Microflow |
| Running Java actions | Microflow |
| File generation or download | Microflow |
| Transactional commits (rollback on error) | Microflow |
| Background scheduled logic | Microflow |

**Rule of thumb:** A nanoflow runs before the server call. A microflow IS the server call.

## Key Differences from Microflows

| Aspect | Microflow | Nanoflow |
|--------|-----------|----------|
| **Execution** | Server-side | Client-side (browser/mobile) |
| **Database access** | Full | No direct access |
| **Transactions** | Supported | Not supported |
| **Java actions** | Supported | Not supported |
| **JavaScript actions** | Not supported | Supported |
| **SYNCHRONIZE** | Not available | Available (offline sync) |
| **File downloads** | Supported | Not supported |
| **Error handling** | Full `ON ERROR` blocks + `RAISE ERROR` | Per-action `ON ERROR` supported; `RAISE ERROR` / `ErrorEvent` forbidden |
| **Offline** | Not available | Available |
| **Binary return type** | Supported | Not supported |

## Nanoflow Structure

```mdl
/**
 * Nanoflow description
 *
 * @param $Parameter1 Description
 * @returns Description of return value
 */
CREATE [OR MODIFY] NANOFLOW Module.NAV_Name (
  $Parameter1: type
)
RETURNS ReturnType
FOLDER 'FolderPath'
BEGIN
  -- Nanoflow logic here
  RETURN $Result;
END;
```

**Every body statement ends with a semicolon `;`** — required, not optional, exactly as
in microflows. That includes block terminators: `end if;`, `end loop;`, `end while;`,
`end case;`. A missing one is a parse error (`missing ';' at 'return'`), not a warning.

## Naming Convention

Nanoflow names use the `NAV_` prefix by convention:
- `NAV_ValidateCart` — client-side validation
- `NAV_ShowDetails` — page navigation
- `NAV_ToggleFilter` — UI state toggle
- `NAV_SignIn` — authentication
- `NAV_SyncChanges` — offline sync

## Supported Activities

### Object Operations (in-memory only)
```mdl
$Item = CREATE Sales.CartItem (Quantity = 1);
CHANGE $Item (Quantity = $Item/Quantity + 1);
COMMIT $Item;
DELETE $Item;
ROLLBACK $Item;
```

### Calling Other Flows
```mdl
$Result = CALL NANOFLOW Sales.NAV_ValidateCart (Cart = $Cart);
$ServerResult = CALL MICROFLOW Sales.ACT_SubmitOrder (Order = $Order);
$JsResult = CALL JAVASCRIPT ACTION NanoflowCommons.SignIn (userName = $Name, password = $Pass);
```

### UI Activities
```mdl
SHOW PAGE Sales.CartDetail (Cart = $Cart);
CLOSE PAGE;
SHOW MESSAGE WARNING 'Connection unavailable. Working offline.';
VALIDATION FEEDBACK $Item/Quantity MESSAGE 'Quantity must be at least 1';
```

### Logging and Variables
```mdl
LOG INFO 'Cart updated with ' + toString($ItemCount) + ' items';
DECLARE $IsValid Boolean = true;
SET $IsValid = false;
```

**Where nanoflow log output goes (and a filtering trap):** a nanoflow runs on the
client, so its `LOG` output takes two paths:

- **Browser console** — with automatic timing, e.g.
  `[Nanoflow] [flow_…] Starting execution of Sudoku.NF_ToggleNotes` … `Finished … 6.7 ms`.
- **Server runtime log** (`.mxcli/runtime.log` under `run --local`) — but the runtime
  **rewrites the log node** to `Client_Nanoflow`. So `LOG INFO NODE 'Sudoku' '…'` from a
  nanoflow appears as `Client_Nanoflow: …`, **not** `Sudoku: …`. A log filter built
  around your microflow node names will silently drop every nanoflow line — grep for
  `Client_Nanoflow` (or the message text) to see nanoflow logs. This is a Mendix
  platform behaviour, not an mxcli one.
- **`LOG DEBUG` never reaches the server log.** Only `INFO`/`WARNING`/`ERROR` reach
  `runtime.log`; a nanoflow `LOG DEBUG` line is sent but dropped server-side. All four
  levels still show in the **browser console**, so use the console (not `runtime.log`)
  when debugging at `DEBUG` level.

### Control Flow
```mdl
IF $Cart/ItemCount = 0 THEN
  VALIDATION FEEDBACK $Cart/ItemCount MESSAGE 'Cart is empty';
  RETURN false;
ELSE
  SHOW PAGE Sales.Checkout (Cart = $Cart);
  RETURN true;
END IF;
```

### Offline Sync
```mdl
-- Sync uncommitted changes back to server
SYNCHRONIZE $Item;
```
`SYNCHRONIZE` is nanoflow-only. Use it after committing offline objects to push changes to the server in a native mobile context.

---

## Real-World Patterns

These patterns come from 223 nanoflows across three production Mendix apps:
EnquiriesManagement (79), Evora-FactoryManagement (93), LatoProductInventory (51).

### Pattern 1: Client-Side Validation

Validate before calling a microflow to avoid a round-trip.

```mdl
mdl 1;
/**
 * Validates the enquiry form fields before submission.
 * @param $Enquiry The enquiry being created or edited
 * @returns true if valid, false if validation errors were shown
 */
CREATE OR MODIFY NANOFLOW Enquiries.NAV_ValidateEnquiry (
  $Enquiry: Enquiries.Enquiry
)
RETURNS Boolean
FOLDER 'Validation'
BEGIN
  IF $Enquiry/Subject = '' THEN
    VALIDATION FEEDBACK $Enquiry/Subject MESSAGE 'Subject is required';
    RETURN false;
  END IF;
  IF $Enquiry/ContactEmail = '' THEN
    VALIDATION FEEDBACK $Enquiry/ContactEmail MESSAGE 'Email is required';
    RETURN false;
  END IF;
  RETURN true;
END;
```

### Pattern 2: Navigation Controller

Validate then navigate — keeps pages dumb.

```mdl
mdl 1;
/**
 * Validates the product and opens the detail page if valid.
 * @param $Product Product to open
 */
CREATE OR MODIFY NANOFLOW Inventory.NAV_OpenProductDetail (
  $Product: Inventory.Product
)
FOLDER 'Navigation'
BEGIN
  $IsValid = CALL NANOFLOW Inventory.NAV_ValidateProduct (Product = $Product);
  IF NOT ($IsValid) THEN
    RETURN;
  END IF;
  SHOW PAGE Inventory.ProductDetail (Product = $Product);
END;
```

### Pattern 3: UI Feedback Wrapper

Wrap a slow server call with progress indicators.

```mdl
/**
 * Shows progress, calls the server, hides progress.
 * @param $Order The order to submit
 */
CREATE OR MODIFY NANOFLOW Sales.NAV_SubmitOrderWithProgress (
  $Order: Sales.Order
)
FOLDER 'Actions'
BEGIN
  CALL JAVASCRIPT ACTION NanoflowCommons.ShowProgress (message = 'Submitting order...');
  $Result = CALL MICROFLOW Sales.ACT_SubmitOrder (Order = $Order);
  CALL JAVASCRIPT ACTION NanoflowCommons.HideProgress ();
  IF $Result THEN
    SHOW MESSAGE SUCCESS 'Order submitted successfully.';
  END IF;
END;
```

### Pattern 4: Confirmation Dialog Before Destructive Action

```mdl
mdl 1;
/**
 * Asks the user to confirm before deleting an item.
 * @param $Item The inventory item to delete
 */
CREATE OR MODIFY NANOFLOW Inventory.NAV_ConfirmDeleteItem (
  $Item: Inventory.Item
)
FOLDER 'Actions'
BEGIN
  $Confirmed = CALL JAVASCRIPT ACTION NanoflowCommons.ShowConfirmation (
    question = 'Delete ' + $Item/Name + '?',
    positiveButtonCaption = 'Delete',
    cancelButtonCaption = 'Cancel'
  );
  IF NOT ($Confirmed) THEN
    RETURN;
  END IF;
  CALL MICROFLOW Inventory.ACT_DeleteItem (Item = $Item);
  CLOSE PAGE;
END;
```

### Pattern 5: Authentication Flow

```mdl
/**
 * Signs the user in and navigates to the home page on success.
 * @param $Username Login username
 * @param $Password Login password
 */
CREATE OR MODIFY NANOFLOW Auth.NAV_SignIn (
  $Username: String,
  $Password: String
)
FOLDER 'Authentication'
BEGIN
  $StatusCode = CALL JAVASCRIPT ACTION NanoflowCommons.SignIn (
    userName = $Username,
    password = $Password,
    useAuthToken = true
  );
  IF $StatusCode = 200 THEN
    SHOW PAGE Home.HomePage ();
  ELSE IF $StatusCode = 401 THEN
    SHOW MESSAGE ERROR 'Incorrect username or password.';
  ELSE
    SHOW MESSAGE WARNING 'Could not connect to server (status: ' + toString($StatusCode) + ')';
  END IF;
END;
```

### Pattern 6: Connectivity Check Before Server Call

Common in field-service and factory-management apps where offline is normal.

```mdl
/**
 * Submits inspection results, warns user if offline.
 * @param $Inspection The inspection to submit
 */
CREATE OR MODIFY NANOFLOW Factory.NAV_SubmitInspection (
  $Inspection: Factory.Inspection
)
FOLDER 'Inspections'
BEGIN
  $IsOnline = CALL JAVASCRIPT ACTION NanoflowCommons.IsConnectedToServer ();
  IF NOT ($IsOnline) THEN
    SHOW MESSAGE WARNING 'You are offline. Changes will sync when reconnected.';
    COMMIT $Inspection;
    RETURN;
  END IF;
  COMMIT $Inspection;
  SYNCHRONIZE $Inspection;
  CALL MICROFLOW Factory.ACT_ProcessInspection (Inspection = $Inspection);
END;
```

### Pattern 7: Geolocation Capture

Used in enquiry and field-service apps to tag records with GPS coordinates.

```mdl
mdl 1;
/**
 * Captures the current GPS position and stores it on the record.
 * @param $Record The record to tag with location
 */
CREATE OR MODIFY NANOFLOW Enquiries.NAV_CaptureLocation (
  $Record: Enquiries.SiteVisit
)
FOLDER 'Location'
BEGIN
  $Location = CALL JAVASCRIPT ACTION NanoflowCommons.GetCurrentLocation (
    timeout = 10000,
    maximumAge = 0,
    highAccuracy = true
  );
  CHANGE $Record (
    Latitude = $Location/Latitude,
    Longitude = $Location/Longitude,
    LocationTimestamp = $Location/Timestamp
  );
END;
```

### Pattern 8: Platform-Conditional Logic

```mdl
mdl 1;
/**
 * Opens a map or shows coordinates depending on platform.
 * @param $Latitude Latitude coordinate
 * @param $Longitude Longitude coordinate
 */
CREATE OR MODIFY NANOFLOW Enquiries.NAV_ShowOnMap (
  $Latitude: Decimal,
  $Longitude: Decimal
)
FOLDER 'Location'
BEGIN
  $Platform = CALL JAVASCRIPT ACTION NanoflowCommons.GetPlatform ();
  IF $Platform = 'Native_mobile' THEN
    CALL JAVASCRIPT ACTION NanoflowCommons.OpenMap (
      latitude = $Latitude,
      longitude = $Longitude
    );
  ELSE
    SHOW PAGE Enquiries.LocationDetail (Lat = $Latitude, Lon = $Longitude);
  END IF;
END;
```

### Pattern 9: Local Storage Cache

Store a frequently accessed value locally to avoid a round-trip.

```mdl
mdl 1;
/**
 * Loads the last-used filter value from local storage.
 * @returns The cached filter string, or empty if never set
 */
CREATE OR MODIFY NANOFLOW Inventory.NAV_LoadFilterCache ()
RETURNS String
FOLDER 'Filters'
BEGIN
  $Exists = CALL JAVASCRIPT ACTION NanoflowCommons.StorageItemExists (
    key = 'InventoryFilter'
  );
  IF NOT ($Exists) THEN
    RETURN '';
  END IF;
  $FilterValue = CALL JAVASCRIPT ACTION NanoflowCommons.GetStorageItemString (
    key = 'InventoryFilter'
  );
  RETURN $FilterValue;
END;
```

### Pattern 10: Device Communication (Field Apps)

```mdl
/**
 * Lets the user call the on-site contact directly.
 * @param $Contact The site contact to call
 */
CREATE OR MODIFY NANOFLOW Factory.NAV_CallContact (
  $Contact: Factory.Contact
)
FOLDER 'Communication'
BEGIN
  IF $Contact/Phone = '' THEN
    SHOW MESSAGE WARNING 'No phone number on record for ' + $Contact/Name;
    RETURN;
  END IF;
  CALL JAVASCRIPT ACTION NanoflowCommons.CallPhoneNumber (
    phoneNumber = $Contact/Phone
  );
END;
```

---

## NanoflowCommons JavaScript Actions Reference

All three test apps include the `NanoflowCommons` module. The actions below are available in any app that has this module installed.

### Authentication & Connectivity

| Action | Parameters | Returns | Notes |
|--------|-----------|---------|-------|
| `NanoflowCommons.SignIn` | `userName`, `password`, `useAuthToken` | Integer | 200=success, 401=bad creds, 0=offline |
| `NanoflowCommons.SignOut` | — | — | Logs out current user |
| `NanoflowCommons.IsConnectedToServer` | — | Boolean | false when offline |

### Device Features

| Action | Parameters | Returns | Notes |
|--------|-----------|---------|-------|
| `NanoflowCommons.GetCurrentLocation` | `timeout`, `maximumAge`, `highAccuracy` | `NanoflowCommons.Geolocation` | Object has Lat, Lon, Accuracy, Timestamp |
| `NanoflowCommons.Geocode` | `address`, `provider`, `apiKey` | `NanoflowCommons.Geolocation` | Address → coordinates |
| `NanoflowCommons.ReverseGeocode` | `latitude`, `longitude`, `provider`, `apiKey` | String | Coordinates → address |
| `NanoflowCommons.GetStraightLineDistance` | `fromLat`, `fromLon`, `toLat`, `toLon`, `distanceUnit` | Decimal | Haversine distance |
| `NanoflowCommons.OpenMap` | `latitude`, `longitude` | — | Opens maps app |
| `NanoflowCommons.CallPhoneNumber` | `phoneNumber` | — | Opens dialer |
| `NanoflowCommons.SendTextMessage` | `phoneNumber` | — | Opens SMS app |
| `NanoflowCommons.DraftEmail` | `to`, `subject`, `body` | — | Opens email client |
| `NanoflowCommons.Share` | `content` | — | Native share dialog |
| `NanoflowCommons.OpenURL` | `url` | — | Opens in browser |
| `NanoflowCommons.GetPlatform` | — | String | `Web`, `Native_mobile`, `Hybrid_mobile` |

### UI & Navigation

| Action | Parameters | Returns | Notes |
|--------|-----------|---------|-------|
| `NanoflowCommons.ShowProgress` | `message` | — | Shows loading overlay |
| `NanoflowCommons.HideProgress` | — | — | Hides loading overlay |
| `NanoflowCommons.ShowConfirmation` | `question`, `positiveButtonCaption`, `cancelButtonCaption` | Boolean | true = confirmed |
| `NanoflowCommons.NavigateTo` | `target` | — | Programmatic navigation |
| `NanoflowCommons.ToggleSidebar` | — | — | Show/hide sidebar |

### Local Storage

| Action | Parameters | Returns | Notes |
|--------|-----------|---------|-------|
| `NanoflowCommons.SetStorageItemString` | `key`, `value` | — | Persist string locally |
| `NanoflowCommons.GetStorageItemString` | `key` | String | Read stored string |
| `NanoflowCommons.SetStorageItemObject` | `key`, `value` | — | Persist Mendix object |
| `NanoflowCommons.GetStorageItemObject` | `key`, `entity` | Object | Read stored object |
| `NanoflowCommons.StorageItemExists` | `key` | Boolean | Check before reading |
| `NanoflowCommons.RemoveStorageItem` | `key` | — | Delete a stored item |
| `NanoflowCommons.ClearLocalStorage` | — | — | Clear everything |

### Object Utilities

| Action | Parameters | Returns | Notes |
|--------|-----------|---------|-------|
| `NanoflowCommons.GetGuid` | `object` | String | GUID of Mendix object |
| `NanoflowCommons.GetObjectByGuid` | `entity`, `guid` | Object | Retrieve by GUID |
| `NanoflowCommons.RefreshObject` | `object` | — | Refresh without page reload |
| `NanoflowCommons.RefreshEntity` | `entity` | — | Refresh all objects of type |

### Utilities

| Action | Parameters | Returns | Notes |
|--------|-----------|---------|-------|
| `NanoflowCommons.Wait` | `milliseconds` | — | Async delay |
| `NanoflowCommons.TimeBetween` | `startDate`, `endDate`, `unit` | Decimal | Time difference |
| `NanoflowCommons.GenerateUniqueID` | — | String | Session-scoped unique ID |
| `NanoflowCommons.Base64Encode` | `value` | String | Encode string to Base64 |
| `NanoflowCommons.Base64Decode` | `value` | String | Decode Base64 string |

---

## Disallowed Activities

These will produce validation errors:
- `RAISE ERROR` / `ErrorEvent` — not available in nanoflows
- `CALL JAVA ACTION` — Java actions cannot run client-side
- `EXECUTE DATABASE QUERY` — direct SQL requires server
- `CALL EXTERNAL ACTION` — external actions are server-side
- `SHOW HOME PAGE` — home page navigation is server-side
- `CALL REST SERVICE` / `SEND REST REQUEST` — REST calls are server-side
- `IMPORT FROM MAPPING` / `EXPORT TO MAPPING` — mapping operations are server-side
- `TRANSFORM JSON` — JSON transformations are server-side
- `DOWNLOAD FILE` — file downloads require server-side processing

### Nanoflow-only: SYNCHRONIZE

`SYNCHRONIZE` is the mirror image of the list above — it is allowed **only** in a
nanoflow, because offline synchronization is a client-side operation. The same
statement in a microflow fails the build with CE0009 "This action is not
supported in microflows."; mxcli reports it as **MDL057** before you get there.

```sql
synchronize all;                 -- the whole offline database
synchronize unsynchronized;      -- only objects with uncommitted offline changes (Mendix 9.4+)
synchronize $Order, $Lines;      -- named objects/lists ("Specific" mode)

synchronize all on error continue;
synchronize all on error without rollback begin
  log error 'sync failed';
end error;
```

The mode is always written out, including `all` — the statement says what it does
rather than relying on the reader knowing the platform default.

**Naming trap:** the variable form is stored as `Specific`, not `Selected`, even
though Studio Pro's UI calls it "Selected object(s)". Storage enums come from the
Model SDK's `SynchronizationType`, never from the UI wording.
- All **workflow actions** (11 types: CallWorkflow, OpenWorkflow, SetTaskOutcome, etc.)

## Return Type Restrictions

Binary return type is NOT allowed in nanoflows.

## Error Handling in Nanoflows

Since `RAISE ERROR` is forbidden, handle errors per-action with `ON ERROR`:

```mdl
$Location = CALL JAVASCRIPT ACTION NanoflowCommons.GetCurrentLocation (
  timeout = 5000,
  maximumAge = 0,
  highAccuracy = false
) ON ERROR CONTINUE;

IF $Location = empty THEN
  SHOW MESSAGE WARNING 'Could not get your location.';
  RETURN;
END IF;
```

For per-action error handling on a call, use a handler WITHOUT rollback — the
only form a nanoflow call takes:
```mdl
$Result = CALL NANOFLOW Sales.NAV_Risky () ON ERROR WITHOUT ROLLBACK BEGIN
  LOG WARNING NODE 'Sales' 'Could not load the data.';
END ERROR;
```

### Which activities take which clause in a nanoflow

Measured on Mendix 11.14.0; every other form is **CE6035** "Error handling type
is not supported", and mxcli refuses it (MDL091):

| Activity in a nanoflow | Accepted clauses |
|---|---|
| `DECLARE`, `SET`, `RETRIEVE`, `DELETE` | every form |
| `CREATE`, `COMMIT`, `CALL NANOFLOW`, `CALL MICROFLOW` | only `ON ERROR WITHOUT ROLLBACK BEGIN … END ERROR` |
| `CHANGE`, `LOG`, `SHOW PAGE`, `CLOSE PAGE`, `SHOW MESSAGE`, `VALIDATION FEEDBACK` | none |

mxcli refuses the clause rather than writing a nanoflow mxbuild rejects. The
split is by activity, not by "client-side vs server-side" — `SHOW MESSAGE` is as
client-side as it gets and still refuses one.

A nanoflow activity **aborts the flow** on error by default (a nanoflow has no
transaction to roll back), which is why there is nothing to configure. That
default is also why writing `Rollback` there is an error and not a no-op.

## Security (GRANT/REVOKE)

```mdl
mdl 1;
GRANT EXECUTE ON NANOFLOW Shop.NAV_Filter TO Shop.User, Shop.Admin;
REVOKE EXECUTE ON NANOFLOW Shop.NAV_Filter FROM Shop.User;
```

## Management Commands

```mdl
mdl 1;
LIST NANOFLOWS;
LIST NANOFLOWS IN MyModule;
DESCRIBE NANOFLOW MyModule.NAV_ShowDetails;
DROP NANOFLOW MyModule.NAV_ShowDetails;
RENAME NANOFLOW MyModule.NAV_OldName TO NAV_NewName;
MOVE NANOFLOW Sales.NAV_OpenCart TO FOLDER 'UI/Navigation';
LIST ACCESS ON NANOFLOW MyModule.NAV_ShowDetails;
```

## Common Mistakes

1. **Using Java actions** — Use CALL JAVASCRIPT ACTION instead.
2. **Using RAISE ERROR** — Nanoflows cannot raise errors directly. Handle per-action with ON ERROR or guard with IF checks.
3. **Expecting transactions** — Nanoflows have no automatic rollback. Design for idempotency.
4. **File operations** — DOWNLOAD FILE is server-only.
5. **Binary return types** — Not supported in nanoflows.
6. **REST/external calls** — REST calls and external actions are server-only. Call a microflow to do server work.
7. **Calling NanoflowCommons before checking availability** — Always check `StorageItemExists` before reading; always check `IsConnectedToServer` before syncing.
8. **Using SYNCHRONIZE outside native mobile context** — Only call SYNCHRONIZE in native mobile offline flows.

## Validation Checklist

- [ ] No RAISE ERROR / ErrorEvent
- [ ] No Java action calls
- [ ] No REST calls, external action calls, or database queries
- [ ] No file download operations
- [ ] No import/export mapping or JSON transformation
- [ ] No workflow actions
- [ ] No show home page
- [ ] No binary return type
- [ ] Parameters and return types are nanoflow-compatible
- [ ] JavaDoc documentation present
- [ ] NAV_ naming prefix used
- [ ] NanoflowCommons actions guarded (connectivity check, storage exists check)
- [ ] Progress indicators hidden on both success and error paths

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…