Don't use nanoflows for web applications
Posted in Mendix on Oct 18, 2023 · 5 min read
But (there is always a but)… it can lead to quite a number of security and performance issues. That’s why I’m suggesting the following (best) practice when developing for web: avoid nanoflows, use microflows whenever you can. This post will discuss the security and performance implications of using nanoflows for web applications.
The prevailing argument for nanoflows is the speed benefit for logic that does not need access to the server. While this in itself is true, the majority of actions in nanoflows do require server interactions1. When multiple nanoflow actions require server interaction (whether it’s a Commit object or a Microflow call) the performance gain quickly becomes the opposite: a significant reduction in responsiveness and performance loss. Instead of one server interaction (calling the microflow that handles all actions) it becomes a series of server requests each with data to send and receive, and amplifying the time it takes for a server round trip to complete.
The Round Trip Time (RTT) is the length time it takes for a request (data) to be sent to a server plus the time it takes processing that request and to receive a response back. There are several factors influencing the round trip time like: distance, network hops, traffic levels and server response time. The speed of a nanoflow that does multiple server interactions will mainly be determined by the total round trip time and much less by the server response time. Therefore, pay attention which actions the nanoflow performs: do they contain server interactions: use a microflow. Is it only client actions (highly unlikely), you can consider a nanoflow.
A theoretical comparison, note: this is only to illustrate the impact adding multiple server interactions in a nanoflow has on the total execution time:
The logic consists of 5 actions, 4 of which require server interaction and 1 doesn't (like a close page) Each server action takes 50ms to process. A round trip takes 200ms. A nanoflow would cost: (50+200)*4 = 1000ms (the 5th action is executed in the browser and is negligible for this example) A microflow would cost: (50*5)+200 = 450ms
This example illustrates that multiple round trips (requests to the server) quickly add up and a Microflow (most likely) provides a better experience.
Actions executed on objects by a nanoflow are subjected to access rules defined on the entity. Microflows, unlike nanoflows, that perform actions on objects are not constrained by the access rules2. This means that if you want to create or delete an object, change attribute values, set an association in a nanoflow you need to allow these actions in the entity’s access rules. As a consequence your security is not least privileged, but allows more access than required for the application to function.
Any action executed in the nanoflow, like calculating the total price, creating a new survey object, is not limited to this nanoflow but can also be executed by an user via the Mendix Client API. Because the security on entities needs to be more lenient for the nanoflow to function, the user gets a lot of control over the data. In the example of a nanoflow calculating the total price and setting that on an Invoice object, the user needs write access to the TotalPrice attribute and can change the value regardless of what the nanoflow has calculated. Because the user can send requests to the server changing that value and the security rules allow it. This is of course undesired behaviour and poses serious security threats.
Because stricter security might prevent a nanoflow from performing its intended behaviour, many unexperienced low-code developers will be inclined to set the security to allow write and read on (all) attributes & associations. Creating vulnerable applications and security risks. The risk is bigger than incorrect Invoice values, it might allow attackers to delete large amounts of data, creating data and using that data to invoke other actions (like microflows) and privilege escalation.
The Mendix developer should work according to the principle of least privilege; users are only able to access the data and resources that are absolutely necessary for its legitimate purpose. Performing the same logic in a microflow instead of a nanoflow, allows the developer set the entity access rules very strict. The microflow is executed on the server and doesn’t check the access rules unless specifically set in the properties, which means you can calculate and set the Invoice TotalPrice in a microflow without setting write access for the user on that attribute. The user can read its value, can invoke the microflow, but the user can not change the outcome.
When to use nanoflows
Nanoflows can still serve a purpose in web application development:
- for actions that don’t need any server interaction
In those cases handle those actions in a nanoflow, but call a microflow to perform the actions that should be executed on the server (most object related actions).
Try to avoid multiple microflow calls in a single nanoflow, like: VAL_Invoice, Invoice_CalcTotalPrice, Invoice_Save. This will cause multiple round trips and also has security issues — allowing to commit an invoice directly by invoking Invoice_Save independently from the two other microflows.
Nanoflows are tricky, both from a security and performance perspective. I recommend to default to microflows and only use nanoflows for web applications when it is absolutely necessary or clear that it delivers a performance gain without sacrificing security.