8.4 Calling Mule Flows from DataWeave & Reusing DataWeave Modules
Key Takeaways
Mule::lookup(flowName, payload, timeoutMillis) executes a flow from inside a DataWeave script and returns that flow's resulting payload.
lookup cannot call a subflow — the target must be a
<flow>, and only the payload is passed: attributes and variables do not travel with the call.The default lookup timeout is 2000 ms on CPU_LITE and CPU_INTENSIVE threads; exceeding it raises an error inside the transformation.
MuleSoft deprecated lookup in favour of a Flow Reference with the target attribute, which keeps orchestration in the flow where it can be traced and error-handled.
Reusable DataWeave lives in .dwl files under src/main/resources/modules and is pulled in with import … from dw::…, giving one definition for many transformations.
Calling Mule Flows from DataWeave & Reusing DataWeave Modules
Most DataWeave scripts are pure: data in, data out. But the exam blueprint contains an objective that breaks that assumption — call Mule flows from a DataWeave script — and a second one about defining and reusing modules, functions, and variables. Both are about the same underlying idea: stop duplicating logic, and reach for shared behavior from inside a transformation.
1. The Mule::lookup Function
The lookup function lives in the Mule module, which DataWeave auto-imports inside a Mule application, so both lookup(...) and the fully qualified Mule::lookup(...) resolve.
lookup(flowName: String, payload: Any, timeoutMillis: Number = 2000): Any
| Parameter | Type | Purpose |
|---|---|---|
flowName | String | Name of the flow to execute, as a literal string |
payload | Any | The payload handed to the target flow |
timeoutMillis | Number | Optional; defaults to 2000 ms on CPU_LITE / CPU_INTENSIVE threads |
<flow name="enrich-order-flow">
<http:listener config-ref="HTTP_Listener_config" path="/orders" doc:name="Listener"/>
<ee:transform doc:name="Enrich With Tax Rate">
<ee:message>
<ee:set-payload><![CDATA[%dw 2.0
output application/json
---
{
orderId: payload.orderId,
subtotal: payload.subtotal,
taxRate: Mule::lookup("getTaxRateFlow", { country: payload.country }).rate
}]]></ee:set-payload>
</ee:message>
</ee:transform>
</flow>
<flow name="getTaxRateFlow">
<http:request method="GET" config-ref="Tax_API_Config" path="/rates" doc:name="Get Rate"/>
</flow>
The transformation calls getTaxRateFlow with a small object, that flow performs the HTTP call, and its resulting payload — { "rate": 0.0825 } — comes back as the value of the lookup expression.
2. The Four Restrictions That Get Tested
+-----------------------------------------------------------------------------------------+
| WHAT CROSSES THE lookup BOUNDARY |
| |
| CALLING TRANSFORMATION TARGET FLOW |
| payload ---- (only the 2nd argument) ---> payload YES |
| attributes ---- X -------------------------> attributes NO (not transmitted) |
| vars ---- X -------------------------> vars NO (not transmitted) |
| |
| RETURN: target flow payload ONLY <-------- (attributes/vars discarded) |
+-----------------------------------------------------------------------------------------+
- Flows only, never subflows.
lookupcannot invoke a<sub-flow>. Passing a subflow name is an error, and "call the subflow with lookup" is a reliable wrong answer. - Payload only, in both directions. Attributes and variables are not transmitted into the target flow, and the target flow returns only its payload. A target flow that reads
attributes.queryParamswill find nothing there. - It is bounded by a timeout. The default is 2000 ms when the transformation runs on a CPU_LITE or CPU_INTENSIVE thread (one minute on other thread types). A slow downstream call inside the target flow surfaces as a timeout raised inside the transformation, which is a confusing place to debug.
- Execution timing is not guaranteed. DataWeave is functional and lazily evaluated, so a
lookupwhose result is never used may not run at all, and multiple lookups may run in parallel. That makeslookupunsuitable for anything with side effects — never use it to write to a database or publish a message.
Warning
lookup Is Deprecated — Know Both the Function and the Preferred Alternative
MuleSoft deprecates lookup and recommends a Flow Reference with the target attribute instead: <flow-ref name="getTaxRateFlow" target="taxRate"/> puts the result in vars.taxRate where the transformation can read it, keeps orchestration visible in the flow, and lets normal error handlers catch failures. The exam can test either the function's mechanics or the recommendation, so learn both.
<!-- Preferred: orchestration stays in the flow, result lands in a variable -->
<flow-ref name="getTaxRateFlow" target="taxRate" doc:name="Get Tax Rate"/>
<ee:transform doc:name="Build Response">
<ee:message>
<ee:set-payload><![CDATA[%dw 2.0
output application/json
---
{ orderId: payload.orderId, taxRate: vars.taxRate.rate }]]></ee:set-payload>
</ee:message>
</ee:transform>
3. Reusing DataWeave: Variables, Functions, and Modules
The same blueprint domain expects candidates to define, use, and reuse DataWeave constructs. There are three levels, and questions usually turn on picking the right one.
| Construct | Declared with | Scope | Use when |
|---|---|---|---|
| Header variable | var taxRate = 0.0825 | One script | A constant or precomputed value used repeatedly in that script |
| Header function | fun net(g) = g * 0.9 | One script | Logic reused several times inside that script |
Module (.dwl file) | import … from dw::… | Whole application | Logic reused across several transformations |
Building a Custom Module
A reusable module is a .dwl file placed under src/main/resources/modules/. The file path becomes its import path: src/main/resources/modules/OrderUtils.dwl is imported as modules::OrderUtils.
// src/main/resources/modules/OrderUtils.dwl
%dw 2.0
var STANDARD_DISCOUNT = 0.05
fun applyDiscount(amount: Number, rate: Number = STANDARD_DISCOUNT): Number =
amount - (amount * rate)
fun tierOf(total: Number): String =
if (total >= 10000) "PLATINUM"
else if (total >= 1000) "GOLD"
else "STANDARD"
%dw 2.0
import applyDiscount, tierOf from modules::OrderUtils
output application/json
---
payload map (order) -> {
orderId: order.id,
payable: applyDiscount(order.total),
tier: tierOf(order.total)
}
The Three Import Forms
| Form | Effect |
|---|---|
import dw::core::Strings | Members must be qualified: Strings::capitalize(x) |
import capitalize, words from dw::core::Strings | Named members callable directly: capitalize(x) |
import * from dw::core::Strings | Every member callable directly; risks name collisions |
Important
Modules Are Not Auto-Imported — Except a Few
dw::Core is imported automatically, which is why map, filter, sizeOf, and now() work with no header line. Everything else — dw::core::Strings, dw::core::Arrays, dw::core::Binaries, and your own modules — needs an explicit import. An exam snippet that calls capitalize() with no import is broken, and the missing import is the defect the question is pointing at.
Tip
Reuse Across Applications Goes Through Exchange
A module shared by one application belongs in src/main/resources/modules. Logic shared by many applications belongs in a library published to Anypoint Exchange and pulled in as a Maven dependency, which is the same reuse story as RAML fragments one layer down.
A developer writes Mule::lookup("validateAddressSubFlow", payload) inside a Transform Message component. The target is defined in the application as <sub-flow name="validateAddressSubFlow">. What happens?
The call succeeds only if the subflow is declared in the same XML file as the calling transformation.
The call fails: lookup can only execute a <flow>, and subflows are not valid targets for the function.
The subflow executes normally, but its result is discarded because subflows cannot return a payload.
The subflow executes and returns its payload, because lookup resolves flows and subflows identically.
A Transform Message component calls Mule::lookup("fetchCreditScoreFlow", payload). Inside fetchCreditScoreFlow, a Logger tries to read attributes.queryParams.customerId and vars.correlationRef, both of which exist in the calling flow. What does the target flow see?
The attributes are present but the variables are not, because attributes are immutable and therefore safe to share.
Both values are present only if the target flow is invoked within the default 2000 ms timeout.
Both values are present, because lookup clones the entire Mule event into the target flow.
Neither value is present: lookup transmits only the payload argument, so attributes and variables do not cross the boundary.
A team wants one definition of a customer-tier calculation shared by six different Transform Message components across three XML files in the same Mule project. Which approach matches MuleSoft guidance?
Create a .dwl module under src/main/resources/modules and import the function into each script that needs it.
Wrap the calculation in a subflow and invoke it from each script with Mule::lookup.
Declare the function in the header of each of the six scripts so every transformation stays self-contained.
Store the calculation as a string in an object store and evaluate it dynamically at runtime.
A DataWeave script begins with %dw 2.0 / output application/json and its body calls capitalize(payload.name) and map over payload.items. The script fails to compile on capitalize but the map operation is fine. What is the cause?
capitalize only accepts Array inputs, so calling it on a String is a type error.
The output directive must be application/java before any Strings module function can be invoked.
map comes from dw::Core, which is imported automatically, while capitalize comes from dw::core::Strings, which requires an explicit import.
map is a DataWeave operator while capitalize is a Java method, so capitalize requires the Java Module to be added to pom.xml.
Sections you finish are checked off in the contents.