Mendix Nanoflow Skill

SkillDev tools

Lets your agent write Claude skill files covering Mendix nanoflow syntax in MDL, explain validation errors, and pick nanoflow vs microflow.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Mendix Nanoflow Skill skill

About this capability

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.

What this skill tells your AI

The instructions your AI receives, as published by mendixlabs/mxcli in .claude/skills/mendix/write-nanoflows/SKILL.md and read by ahel’s review.

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 — keep both copies in sync.

When to Use a Nanoflow vs a Microflow

ScenarioUse
Client-side form validation before saveNanoflow
UI navigation and page routingNanoflow
Calling device features (GPS, phone, camera)Nanoflow
Offline data access and local storageNanoflow
Calling JavaScript actions (NanoflowCommons)Nanoflow
Showing progress indicators / confirmation dialogsNanoflow
Querying the databaseMicroflow
Calling REST services or external actionsMicroflow
Running Java actionsMicroflow
File generation or downloadMicroflow
Transactional commits (rollback on error)Microflow
Background scheduled logicMicroflow

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

Key Differences from Microflows

AspectMicroflowNanoflow
ExecutionServer-sideClient-side (browser/mobile)
Database accessFullNo direct access
TransactionsSupportedNot supported
Java actionsSupportedNot supported
JavaScript actionsNot supportedSupported
SYNCHRONIZENot availableAvailable (offline sync)
File downloadsSupportedNot supported
Error handlingFull ON ERROR blocks + RAISE ERRORPer-action ON ERROR supported; RAISE ERROR / ErrorEvent forbidden
OfflineNot availableAvailable
Binary return typeSupportedNot supported

Nanoflow Structure

/**
 * 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)

$Item = CREATE Sales.CartItem (Quantity = 1);
CHANGE $Item (Quantity = $Item/Quantity + 1);
COMMIT $Item;
DELETE $Item;
ROLLBACK $Item;

Calling Other Flows

$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

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

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

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

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

/**
 * 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.

/**
 * 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.

/**
 * 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

/**
 * 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

/**
 * 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.

/**
 * 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.

/**
 * 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

/**
 * 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.

/**
 * 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)

/**
 * 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

ActionParametersReturnsNotes
NanoflowCommons.SignInuserName, password, useAuthTokenInteger200=success, 401=bad creds, 0=offline
NanoflowCommons.SignOut——Logs out current user
NanoflowCommons.IsConnectedToServer—Booleanfalse when offline

Device Features

ActionParametersReturnsNotes
NanoflowCommons.GetCurrentLocationtimeout, maximumAge, highAccuracyNanoflowCommons.GeolocationObject has Lat, Lon, Accuracy, Timestamp
NanoflowCommons.Geocodeaddress, provider, apiKeyNanoflowCommons.GeolocationAddress → coordinates
NanoflowCommons.ReverseGeocodelatitude, longitude, provider, apiKeyStringCoordinates → address
NanoflowCommons.GetStraightLineDistancefromLat, fromLon, toLat, toLon, distanceUnitDecimalHaversine distance
NanoflowCommons.OpenMaplatitude, longitude—Opens maps app
NanoflowCommons.CallPhoneNumberphoneNumber—Opens dialer
NanoflowCommons.SendTextMessagephoneNumber—Opens SMS app
NanoflowCommons.DraftEmailto, subject, body—Opens email client
NanoflowCommons.Sharecontent—Native share dialog
NanoflowCommons.OpenURLurl—Opens in browser
NanoflowCommons.GetPlatform—StringWeb, Native_mobile, Hybrid_mobile

UI & Navigation

ActionParametersReturnsNotes
NanoflowCommons.ShowProgressmessage—Shows loading overlay
NanoflowCommons.HideProgress——Hides loading overlay
NanoflowCommons.ShowConfirmationquestion, positiveButtonCaption, cancelButtonCaptionBooleantrue = confirmed
NanoflowCommons.NavigateTotarget—Programmatic navigation
NanoflowCommons.ToggleSidebar——Show/hide sidebar

Local Storage

ActionParametersReturnsNotes
NanoflowCommons.SetStorageItemStringkey, value—Persist string locally
NanoflowCommons.GetStorageItemStringkeyStringRead stored string
NanoflowCommons.SetStorageItemObjectkey, value—Persist Mendix object
NanoflowCommons.GetStorageItemObjectkey, entityObjectRead stored object
NanoflowCommons.StorageItemExistskeyBooleanCheck before reading
NanoflowCommons.RemoveStorageItemkey—Delete a stored item
NanoflowCommons.ClearLocalStorage——Clear everything

Object Utilities

ActionParametersReturnsNotes
NanoflowCommons.GetGuidobjectStringGUID of Mendix object
NanoflowCommons.GetObjectByGuidentity, guidObjectRetrieve by GUID
NanoflowCommons.RefreshObjectobject—Refresh without page reload
NanoflowCommons.RefreshEntityentity—Refresh all objects of type

Utilities

ActionParametersReturnsNotes
NanoflowCommons.Waitmilliseconds—Async delay
NanoflowCommons.TimeBetweenstartDate, endDate, unitDecimalTime difference
NanoflowCommons.GenerateUniqueID—StringSession-scoped unique ID
NanoflowCommons.Base64EncodevalueStringEncode string to Base64
NanoflowCommons.Base64DecodevalueStringDecode 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.

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 {
  log error 'sync failed';
};

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:

$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 without CONTINUE:

$Result = CALL NANOFLOW Sales.NAV_Risky () ON ERROR ROLLBACK;

Most activities take NO error handling in a nanoflow

An ON ERROR clause of any form is rejected on these six, with CE6035 "Error handling type is not supported" — measured on Mendix 11.14.0:

Refused in a nanoflowAccepted
CHANGE, LOG, SHOW PAGE, CLOSE PAGE, SHOW MESSAGE, VALIDATION FEEDBACKDECLARE, SET (the two variable activities)

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)

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

Management Commands

SHOW NANOFLOWS
SHOW 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';
SHOW 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

Signals

GitHub stars
122
Forks
49
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
write-nanoflows
Source
github.com/mendixlabs/mxcli