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
lookupand recommends a Flow Reference with thetargetattribute instead:<flow-ref name="getTaxRateFlow" target="taxRate"/>puts the result invars.taxRatewhere 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::Coreis imported automatically, which is whymap,filter,sizeOf, andnow()work with no header line. Everything else —dw::core::Strings,dw::core::Arrays,dw::core::Binaries, and your own modules — needs an explicitimport. An exam snippet that callscapitalize()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?
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?
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?
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?