If you're upgrading from Mule 3 to Mule 4, every DataWeave file needs to be rewritten. DW 2.0 is not backwards-compatible with 1.0 — the syntax changed, type names changed, flow control changed, and the module system is completely new.
I've migrated dozens of Mule projects and compiled the most common changes, gotchas, and anti-patterns into a comprehensive migration guide. Here's the practical version.
The Big Syntax Changes
1. Header Declarations Lost the %
DW 1.0 used % prefixes for everything in the header. DW 2.0 dropped them.
| What | DW 1.0 | DW 2.0 |
|---|---|---|
| Version | %dw 1.0 |
%dw 2.0 |
| Output | %output application/json |
output application/json |
| Variables | %var x = 42 |
var x = 42 |
| Functions | %function name(args) |
fun name(args) = |
| Namespaces | %namespace ns http://... |
ns ns http://... |
DW 1.0:
%dw 1.0
%output application/json
%var threshold = 100
%function isActive(emp) emp.status == "ACTIVE"
---
payload filter isActive($) filter $.salary > threshold
DW 2.0:
%dw 2.0
output application/json
var threshold = 100
fun isActive(emp) = emp.status == "ACTIVE"
---
payload filter isActive($) filter $.salary > threshold
Note: %dw 2.0 keeps the % — it's the only header line that does. And fun uses = before the body.
2. Flow Control: when/otherwise to if/else
This is the change that breaks the most code.
DW 1.0:
value: payload.amount when payload.amount > 0 otherwise 0
DW 2.0:
value: if (payload.amount > 0) payload.amount else 0
The logic reads more naturally in 2.0, but every when/otherwise in your codebase needs to be flipped — the condition and value swap positions.
3. Scoped Variables: using to do { }
DW 1.0:
items: payload map ((item) ->
using (total = item.price * item.qty)
{
name: item.name,
total: total,
tax: total * 0.08
}
)
DW 2.0:
items: payload map ((item) ->
do {
var total = item.price * item.qty
---
{
name: item.name,
total: total,
tax: total * 0.08
}
}
)
The do { var ... --- body } block is more verbose but also more powerful — you can define multiple variables, functions, and even types inside a do block.
4. Type Names: Lowercase to Capitalized
Every type reference changed from lowercase with : prefix to capitalized without prefix.
| DW 1.0 | DW 2.0 |
|---|---|
:string |
String |
:number |
Number |
:boolean |
Boolean |
:object |
Object |
:array |
Array |
:date |
Date |
:datetime |
DateTime |
DW 1.0:
payload.price as :number
DW 2.0:
payload.price as Number
5. Null Handling: when/otherwise to default
DW 2.0 introduced the default operator, which replaces the verbose null-checking pattern from 1.0.
DW 1.0:
name: payload.name when payload.name != null otherwise "Unknown"
DW 2.0:
name: payload.name default "Unknown"
default handles the entire null chain. If payload itself is null, payload.name default "Unknown" still returns "Unknown" without throwing an error.
6. String Interpolation (New in 2.0)
DW 1.0 only had concatenation. DW 2.0 added interpolation.
DW 1.0:
greeting: "Hello, " ++ payload.name ++ "! You have " ++ payload.count ++ " items."
DW 2.0:
greeting: "Hello, $(payload.name)! You have $(payload.count) items."
Use $(expression) inside double-quoted strings. Cleaner, more readable, and less error-prone than chaining ++.
7. Date Functions: now to now()
In DW 1.0, now was a keyword. In DW 2.0, it's a function call.
DW 1.0:
timestamp: now as :string {format: "yyyy-MM-dd"}
DW 2.0:
timestamp: now() as String {format: "yyyy-MM-dd"}
New Features in DW 2.0
Don't just translate 1.0 syntax — take advantage of what 2.0 added:
Pattern Matching
payload.status match {
case "ACTIVE" -> "green"
case "PENDING" -> "yellow"
case "INACTIVE" -> "red"
else -> "gray"
}
Module Imports
import * from dw::core::Strings
import * from dw::core::Arrays
---
{
name: capitalize(payload.name),
items: countBy(payload.items, (item) -> item.category)
}
Period/Duration Literals
nextWeek: |2026-02-15| + |P7D|
lastMonth: now() - |P1M|
threeHours: |PT3H|
Enhanced Type System
type Address = {street: String, city: String, zip: String}
type Customer = {name: String, address: Address}
Common Migration Anti-Patterns
These are the mistakes I see most when teams migrate. Don't just translate syntax — fix these patterns while you're at it.
1. Translating when/otherwise for null checks instead of using default
Bad (literal translation):
// Translated from DW 1.0 — works but wasteful
name: if (payload.name != null) payload.name else "Unknown"
Good (idiomatic DW 2.0):
name: payload.name default "Unknown"
2. Not handling repeating XML elements
This bug existed in 1.0 too, but migration is a good time to fix it.
Bad:
// Only returns FIRST item — silent data loss
items: payload.Order.Item
Good:
// Returns ALL items as an array
items: payload.Order.*Item
3. Keeping reduce where simpler functions exist
DW 2.0 has richer built-in functions. Replace verbose reduce calls:
Bad:
total: payload.items reduce ((item, acc = 0) -> acc + item.price)
Good:
total: sum(payload.items.price)
4. Not casting CSV/XML string values
Bad:
// quantity is "5" (string), not 5 (number)
quantity: row.quantity
Good:
quantity: row.quantity as Number
5. Writing recursive functions without @TailRec
Bad:
fun flatten(data) = ... // Stack overflow on deep data
Good:
@TailRec()
fun flatten(data) = ... // Safe for any depth
Migration Checklist
Use this as a quick reference when converting each DW file:
- [ ] Change
%outputtooutput,%vartovar,%functiontofun - [ ] Replace
when ... otherwisewithif ... else(flip condition/value order) - [ ] Replace null checks with
defaultoperator - [ ] Update type names:
:stringtoString,:numbertoNumber, etc. - [ ] Replace
using (...)withdo { var ... --- ... } - [ ] Change
nowtonow()and other function-call syntax - [ ] Replace
++concatenation with$(...)string interpolation where appropriate - [ ] Add
as Number,as Datecasts for CSV/XML fields - [ ] Use
.*Elementfor repeating XML elements - [ ] Look for
reducecalls that can use built-in functions (sum,groupBy,maxBy) - [ ] Add
@TailRec()to any recursive functions - [ ] Test in the DataWeave Playground
Full Reference
This article covers the most common migration changes. For the complete guide with more examples:
- DW 1.0 to 2.0 Migration Guide — every syntax change with side-by-side examples
- Anti-Patterns Guide — 12 common DataWeave mistakes and how to fix them
- DW 2.x Cheatsheet — comprehensive quick reference for the new syntax
- 80+ Production-Ready Patterns — all written in DW 2.0 with realistic examples
Currently migrating from Mule 3 to Mule 4? What's the trickiest DW transformation you've had to convert? Share in the comments.
Top comments (0)