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.

Last updated: August 2026

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
ParameterTypePurpose
flowNameStringName of the flow to execute, as a literal string
payloadAnyThe payload handed to the target flow
timeoutMillisNumberOptional; 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)             |
+-----------------------------------------------------------------------------------------+
  1. Flows only, never subflows. lookup cannot invoke a <sub-flow>. Passing a subflow name is an error, and "call the subflow with lookup" is a reliable wrong answer.
  2. 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.queryParams will find nothing there.
  3. 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.
  4. Execution timing is not guaranteed. DataWeave is functional and lazily evaluated, so a lookup whose result is never used may not run at all, and multiple lookups may run in parallel. That makes lookup unsuitable 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.

ConstructDeclared withScopeUse when
Header variablevar taxRate = 0.0825One scriptA constant or precomputed value used repeatedly in that script
Header functionfun net(g) = g * 0.9One scriptLogic reused several times inside that script
Module (.dwl file)import … from dw::…Whole applicationLogic 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

FormEffect
import dw::core::StringsMembers must be qualified: Strings::capitalize(x)
import capitalize, words from dw::core::StringsNamed members callable directly: capitalize(x)
import * from dw::core::StringsEvery 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.

Test Your Knowledge

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

The call succeeds only if the subflow is declared in the same XML file as the calling transformation.

B

The call fails: lookup can only execute a <flow>, and subflows are not valid targets for the function.

C

The subflow executes normally, but its result is discarded because subflows cannot return a payload.

D

The subflow executes and returns its payload, because lookup resolves flows and subflows identically.

Test Your Knowledge

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

The attributes are present but the variables are not, because attributes are immutable and therefore safe to share.

B

Both values are present only if the target flow is invoked within the default 2000 ms timeout.

C

Both values are present, because lookup clones the entire Mule event into the target flow.

D

Neither value is present: lookup transmits only the payload argument, so attributes and variables do not cross the boundary.

Test Your Knowledge

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

Create a .dwl module under src/main/resources/modules and import the function into each script that needs it.

B

Wrap the calculation in a subflow and invoke it from each script with Mule::lookup.

C

Declare the function in the header of each of the six scripts so every transformation stays self-contained.

D

Store the calculation as a string in an object store and evaluate it dynamically at runtime.

Test Your Knowledge

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?

A

capitalize only accepts Array inputs, so calling it on a String is a type error.

B

The output directive must be application/java before any Strings module function can be invoked.

C

map comes from dw::Core, which is imported automatically, while capitalize comes from dw::core::Strings, which requires an explicit import.

D

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.