<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Shakar Bisetty</title>
    <description>The latest articles on DEV Community by Shakar Bisetty (@thasha).</description>
    <link>https://dev.to/thasha</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3783981%2F7124d594-3898-4f92-ac8d-ec2882dc48de.png</url>
      <title>DEV Community: Shakar Bisetty</title>
      <link>https://dev.to/thasha</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thasha"/>
    <language>en</language>
    <item>
      <title>Use host.docker.internal to reach local Ollama from Mule</title>
      <dc:creator>Shakar Bisetty</dc:creator>
      <pubDate>Sun, 13 Sep 2026 14:58:47 +0000</pubDate>
      <link>https://dev.to/thasha/use-hostdockerinternal-to-reach-local-ollama-from-mule-1p02</link>
      <guid>https://dev.to/thasha/use-hostdockerinternal-to-reach-local-ollama-from-mule-1p02</guid>
      <description>&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;The flow failed with a connection refused error when the configuration used &lt;code&gt;http://localhost:11434/v1&lt;/code&gt; as the base URL. The MuleSoft Inference Connector enables text generation workflows &lt;a href="https://docs.mulesoft.com/mulesoft-inference-connector/latest/" rel="noopener noreferrer"&gt;docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Input
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"prompt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Reply with exactly one word: pong"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fltasp1zppm5alhy9rfge.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fltasp1zppm5alhy9rfge.png" alt="flow of the transform" width="799" height="202"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Working configuration
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;mule&lt;/span&gt; &lt;span class="na"&gt;xmlns=&lt;/span&gt;&lt;span class="s"&gt;"http://www.mulesoft.org/schema/mule/core"&lt;/span&gt;
      &lt;span class="na"&gt;xmlns:http=&lt;/span&gt;&lt;span class="s"&gt;"http://www.mulesoft.org/schema/mule/http"&lt;/span&gt;
      &lt;span class="na"&gt;xmlns:ms-inference=&lt;/span&gt;&lt;span class="s"&gt;"http://www.mulesoft.org/schema/mule/ms-inference"&lt;/span&gt;
      &lt;span class="na"&gt;xmlns:xsi=&lt;/span&gt;&lt;span class="s"&gt;"http://www.w3.org/2001/XMLSchema-instance"&lt;/span&gt;
      &lt;span class="na"&gt;xsi:schemaLocation=&lt;/span&gt;&lt;span class="s"&gt;"
        http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
        http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
        http://www.mulesoft.org/schema/mule/ms-inference http://www.mulesoft.org/schema/mule/ms-inference/current/mule-ms-inference.xsd"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;http:listener-config&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"http-listener-config"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;http:listener-connection&lt;/span&gt; &lt;span class="na"&gt;host=&lt;/span&gt;&lt;span class="s"&gt;"0.0.0.0"&lt;/span&gt; &lt;span class="na"&gt;port=&lt;/span&gt;&lt;span class="s"&gt;"8081"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/http:listener-config&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;ms-inference:text-generation-config&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"ollama-openai-compatible"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ms-inference:openai-compatible-connection&lt;/span&gt;
        &lt;span class="na"&gt;openAICompatibleModelName=&lt;/span&gt;&lt;span class="s"&gt;"qwen2.5:14b"&lt;/span&gt;
        &lt;span class="na"&gt;openAICompatibleURL=&lt;/span&gt;&lt;span class="s"&gt;"http://host.docker.internal:11434/v1"&lt;/span&gt;
        &lt;span class="na"&gt;apiKey=&lt;/span&gt;&lt;span class="s"&gt;"ollama"&lt;/span&gt;
        &lt;span class="na"&gt;maxTokens=&lt;/span&gt;&lt;span class="s"&gt;"16"&lt;/span&gt;
        &lt;span class="na"&gt;temperature=&lt;/span&gt;&lt;span class="s"&gt;"0"&lt;/span&gt;
        &lt;span class="na"&gt;topP=&lt;/span&gt;&lt;span class="s"&gt;"1"&lt;/span&gt;
        &lt;span class="na"&gt;timeout=&lt;/span&gt;&lt;span class="s"&gt;"120"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ms-inference:text-generation-config&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;flow&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"ask"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;http:listener&lt;/span&gt; &lt;span class="na"&gt;config-ref=&lt;/span&gt;&lt;span class="s"&gt;"http-listener-config"&lt;/span&gt; &lt;span class="na"&gt;path=&lt;/span&gt;&lt;span class="s"&gt;"/ask"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ms-inference:chat-answer-prompt&lt;/span&gt; &lt;span class="na"&gt;config-ref=&lt;/span&gt;&lt;span class="s"&gt;"ollama-openai-compatible"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;ms-inference:prompt&amp;gt;&lt;/span&gt;#[payload.prompt]&lt;span class="nt"&gt;&amp;lt;/ms-inference:prompt&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/ms-inference:chat-answer-prompt&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/flow&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/mule&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Output
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"response"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"response"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Pong"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;response&lt;/code&gt; field contains the model output, and &lt;code&gt;status&lt;/code&gt; shows 200.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trap
&lt;/h2&gt;

&lt;p&gt;Using &lt;code&gt;http://localhost:11434/v1&lt;/code&gt; as the base URL causes a connection refused error.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I do now
&lt;/h2&gt;

&lt;p&gt;I configure &lt;code&gt;openAICompatibleURL&lt;/code&gt; to &lt;code&gt;http://host.docker.internal:11434/v1&lt;/code&gt; in the connector configuration.&lt;/p&gt;

&lt;p&gt;Runs shown: Mule 4 Community kernel 4.12.0&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/shakarbisetty/mulesoft-cookbook/tree/main/dataweave/patterns/19-ai-integration" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F508bxfch9tb6wpumw99b.png" alt="MuleSoft patterns, proven and runnable" width="800" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;104 DataWeave patterns + 8 Exchange modules with 208 MUnit tests: github.com/shakarbisetty/mulesoft-cookbook | 60-second walkthroughs: youtube.com/@SanThaParv&lt;/p&gt;

</description>
      <category>mule</category>
      <category>ollama</category>
      <category>inferenceconnector</category>
      <category>docker</category>
    </item>
    <item>
      <title>Partition once versus filter twice for bulk email validation</title>
      <dc:creator>Shakar Bisetty</dc:creator>
      <pubDate>Thu, 10 Sep 2026 03:23:34 +0000</pubDate>
      <link>https://dev.to/thasha/partition-once-versus-filter-twice-for-bulk-email-validation-40m0</link>
      <guid>https://dev.to/thasha/partition-once-versus-filter-twice-for-bulk-email-validation-40m0</guid>
      <description>&lt;h2&gt;
  
  
  The decision
&lt;/h2&gt;

&lt;p&gt;The partition function separates the array into the elements that satisfy the condition from those that do not &lt;a href="https://docs.mulesoft.com/dataweave/latest/dw-arrays-functions-partition" rel="noopener noreferrer"&gt;docs&lt;/a&gt;. The filter function iterates over an array and applies an expression that returns matching values &lt;a href="https://docs.mulesoft.com/dataweave/latest/dw-core-functions-filter" rel="noopener noreferrer"&gt;docs&lt;/a&gt;. Both scripts compute the task to split payload.records into accepted ids and rejected records with a reason, plus a retry count, by an email match.&lt;/p&gt;

&lt;h2&gt;
  
  
  First approach
&lt;/h2&gt;

&lt;p&gt;The partition once approach uses the import line &lt;code&gt;import * from dw::core::Arrays&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%dw 2.0
import * from dw::core::Arrays
output application/json
var split = payload.records partition (r) -&amp;gt;
    (r.email default "") matches /.+@.+\..+/
---
{
  accepted: split.success map (r) -&amp;gt; r.id,
  rejected: split.failure map (r) -&amp;gt; {
    id: r.id,
    reason: "missing or invalid email"
  },
  retryCount: sizeOf(split.failure)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh7vt9xqyq1r2huxlpw4h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fh7vt9xqyq1r2huxlpw4h.png" alt="flow of the transform" width="800" height="334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Second approach
&lt;/h2&gt;

&lt;p&gt;The filter twice approach requires no import.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%dw 2.0
output application/json
var valid = payload.records filter ((r) -&amp;gt; (r.email default "") matches /.+@.+\..+/)
var invalid = payload.records filter ((r) -&amp;gt; not ((r.email default "") matches /.+@.+\..+/))
---
{
  accepted: valid map (r) -&amp;gt; r.id,
  rejected: invalid map (r) -&amp;gt; {
    id: r.id,
    reason: "missing or invalid email"
  },
  retryCount: sizeOf(invalid)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Same input, same output
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"records"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORD-1001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ana@example.com"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORD-1002"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bad-address"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORD-1003"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"raj@example.org"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORD-1004"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORD-1005"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mei@example.net"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both scripts print the same output for this input.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"accepted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"ORD-1001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"ORD-1003"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"ORD-1005"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rejected"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORD-1002"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"missing or invalid email"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORD-1004"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"missing or invalid email"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"retryCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Measured
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Input&lt;/th&gt;
&lt;th&gt;Records&lt;/th&gt;
&lt;th&gt;Script&lt;/th&gt;
&lt;th&gt;Runs&lt;/th&gt;
&lt;th&gt;Min ms&lt;/th&gt;
&lt;th&gt;Median ms&lt;/th&gt;
&lt;th&gt;Max ms&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;small&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;partition once&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;77&lt;/td&gt;
&lt;td&gt;84&lt;/td&gt;
&lt;td&gt;94&lt;/td&gt;
&lt;td&gt;verified in sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;large&lt;/td&gt;
&lt;td&gt;50000&lt;/td&gt;
&lt;td&gt;partition once&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;378&lt;/td&gt;
&lt;td&gt;383&lt;/td&gt;
&lt;td&gt;388&lt;/td&gt;
&lt;td&gt;verified in sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;small&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;filter twice&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;52&lt;/td&gt;
&lt;td&gt;55.5&lt;/td&gt;
&lt;td&gt;62&lt;/td&gt;
&lt;td&gt;verified in sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;large&lt;/td&gt;
&lt;td&gt;50000&lt;/td&gt;
&lt;td&gt;filter twice&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;319&lt;/td&gt;
&lt;td&gt;326.5&lt;/td&gt;
&lt;td&gt;335&lt;/td&gt;
&lt;td&gt;verified in sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;small&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;baseline&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;51.5&lt;/td&gt;
&lt;td&gt;60&lt;/td&gt;
&lt;td&gt;verified in sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;large&lt;/td&gt;
&lt;td&gt;50000&lt;/td&gt;
&lt;td&gt;baseline&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;143&lt;/td&gt;
&lt;td&gt;147&lt;/td&gt;
&lt;td&gt;154&lt;/td&gt;
&lt;td&gt;verified in sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Script&lt;/th&gt;
&lt;th&gt;Input traversals&lt;/th&gt;
&lt;th&gt;Imports&lt;/th&gt;
&lt;th&gt;Lines&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;partition once&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;import * from dw::core::Arrays&lt;/td&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;verified in sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;filter twice&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;verified in sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;baseline&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;none&lt;/td&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;verified in sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;At 5 records the timing ranges did not overlap and partition once measured 77 to 94 ms while filter twice measured 52 to 62 ms.&lt;br&gt;
At 50000 records the timing ranges did not overlap and partition once measured 378 to 388 ms while filter twice measured 319 to 335 ms.&lt;/p&gt;

&lt;h2&gt;
  
  
  When I choose which
&lt;/h2&gt;

&lt;p&gt;At 5 records the timing ranges do not overlap. At 50000 records the timing ranges do not overlap. I choose filter twice because it carried the lower median at both record counts and its two traversals avoid the import line. See how to partition bulk records into accepted and rejected lists &lt;a href="https://dev.to/thasha/partition-bulk-records-into-accepted-and-rejected-lists-2e5"&gt;Partition bulk records into accepted and rejected lists&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Runs shown: DataWeave CLI 2.12.2&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/shakarbisetty/mulesoft-cookbook/tree/main/dataweave/patterns/01-array-manipulation/dataweave-partition-for-bulk-api-responses" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F508bxfch9tb6wpumw99b.png" alt="MuleSoft patterns, proven and runnable" width="800" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;104 DataWeave patterns + 8 Exchange modules with 208 MUnit tests: github.com/shakarbisetty/mulesoft-cookbook | 60-second walkthroughs: youtube.com/@SanThaParv&lt;/p&gt;

</description>
      <category>dataweave</category>
      <category>partition</category>
      <category>filter</category>
      <category>bulkapi</category>
    </item>
    <item>
      <title>dw-validation-utils: 12 DataWeave validation functions, sandbox-verified reference</title>
      <dc:creator>Shakar Bisetty</dc:creator>
      <pubDate>Wed, 09 Sep 2026 22:09:56 +0000</pubDate>
      <link>https://dev.to/thasha/dw-validation-utils-12-dataweave-validation-functions-sandbox-verified-reference-2o5</link>
      <guid>https://dev.to/thasha/dw-validation-utils-12-dataweave-validation-functions-sandbox-verified-reference-2o5</guid>
      <description>&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;Validate incoming data before processing. &lt;a href="https://raw.githubusercontent.com/shakarbisetty/mulesoft-cookbook/8282bd63b0fb8031b20c3956d32f1f56859493af/dataweave/modules/dw-validation-utils/src/main/resources/modules/ValidationUtils.dwl" rel="noopener noreferrer"&gt;docs&lt;/a&gt;&lt;br&gt;
12 reusable validation functions for DataWeave 2.x — field validation, pattern matching, and bulk payload validation. &lt;a href="https://raw.githubusercontent.com/shakarbisetty/mulesoft-cookbook/8282bd63b0fb8031b20c3956d32f1f56859493af/dataweave/modules/dw-validation-utils/README.md" rel="noopener noreferrer"&gt;docs&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The table
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;th&gt;Signature&lt;/th&gt;
&lt;th&gt;Returns / behaviour&lt;/th&gt;
&lt;th&gt;Source&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;isRequired&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fun isRequired(val: Any, fieldName: String): Object =&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;returns &lt;code&gt;name&lt;/code&gt; as &lt;code&gt;{ valid: false, field: "name", error: "name must not be empty" }&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt; as &lt;code&gt;{ valid: false, field: "email", error: "email is required" }&lt;/code&gt;, &lt;code&gt;tags&lt;/code&gt; as &lt;code&gt;{ valid: false, field: "tags", error: "tags must not be empty" }&lt;/code&gt;, and &lt;code&gt;status&lt;/code&gt; as &lt;code&gt;{ valid: true }&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;verified in sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;minLength&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fun minLength(s: String, min: Number, fieldName: String = "field"): Object =&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;returns &lt;code&gt;valid&lt;/code&gt; as &lt;code&gt;false&lt;/code&gt;, &lt;code&gt;field&lt;/code&gt; as &lt;code&gt;"field"&lt;/code&gt;, and &lt;code&gt;error&lt;/code&gt; as &lt;code&gt;"field must be at least 5 characters"&lt;/code&gt; for a string shorter than the limit without a custom field name&lt;/td&gt;
&lt;td&gt;verified in sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;maxLength&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fun maxLength(s: String, max: Number, fieldName: String = "field"): Object =&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;returns &lt;code&gt;valid&lt;/code&gt; as &lt;code&gt;false&lt;/code&gt;, &lt;code&gt;field&lt;/code&gt; as &lt;code&gt;"name"&lt;/code&gt;, and &lt;code&gt;error&lt;/code&gt; as &lt;code&gt;"name must not exceed 10 characters"&lt;/code&gt; when the input exceeds the limit&lt;/td&gt;
&lt;td&gt;verified in sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;inRange&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fun inRange(n: Number, min: Number, max: Number, fieldName: String = "field"): Object =&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;returns &lt;code&gt;inside&lt;/code&gt; as &lt;code&gt;{ valid: true }&lt;/code&gt;, &lt;code&gt;boundary&lt;/code&gt; as &lt;code&gt;{ valid: true }&lt;/code&gt;, and &lt;code&gt;outside&lt;/code&gt; as &lt;code&gt;{ valid: false, field: "age", error: "age must be between 10 and 100" }&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;verified in sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;matchesPattern&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fun matchesPattern(s: String, regex: String, fieldName: String = "field"): Object =&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;returns &lt;code&gt;validMatch&lt;/code&gt; as &lt;code&gt;{ valid: true }&lt;/code&gt; and &lt;code&gt;invalidMatch&lt;/code&gt; as &lt;code&gt;{ valid: false, field: "invalidCode", error: "invalidCode does not match required pattern" }&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;verified in sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;isValidDate&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fun isValidDate(s: String, fmt: String, fieldName: String = "field"): Object =&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;returns &lt;code&gt;validCheck&lt;/code&gt; as &lt;code&gt;{ valid: true }&lt;/code&gt; and &lt;code&gt;invalidCheck&lt;/code&gt; as &lt;code&gt;{ valid: false, field: "invalidDate", error: "invalidDate is not a valid date (expected format: yyyy-MM-dd)" }&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;verified in sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;isOneOf&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fun isOneOf(val: Any, allowed: Array, fieldName: String = "field"): Object =&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;returns &lt;code&gt;valid&lt;/code&gt; as &lt;code&gt;false&lt;/code&gt;, &lt;code&gt;field&lt;/code&gt; as &lt;code&gt;"status"&lt;/code&gt;, and &lt;code&gt;error&lt;/code&gt; as &lt;code&gt;"status must be one of: ACTIVE, INACTIVE, PENDING"&lt;/code&gt; when the value falls outside the set&lt;/td&gt;
&lt;td&gt;verified in sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;isUUID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fun isUUID(s: String): Boolean =&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;returns &lt;code&gt;valid&lt;/code&gt; as &lt;code&gt;true&lt;/code&gt;, &lt;code&gt;nil&lt;/code&gt; as &lt;code&gt;true&lt;/code&gt;, and &lt;code&gt;invalid&lt;/code&gt; as &lt;code&gt;false&lt;/code&gt; for the respective inputs&lt;/td&gt;
&lt;td&gt;verified in sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;isURL&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fun isURL(s: String): Boolean =&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;returns &lt;code&gt;validUrl&lt;/code&gt; as &lt;code&gt;true&lt;/code&gt; and &lt;code&gt;invalidWord&lt;/code&gt; as &lt;code&gt;false&lt;/code&gt; for the respective inputs&lt;/td&gt;
&lt;td&gt;verified in sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;isPhone&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fun isPhone(s: String): Boolean =&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;returns &lt;code&gt;e164&lt;/code&gt; as &lt;code&gt;true&lt;/code&gt; and &lt;code&gt;sixteenDigits&lt;/code&gt; as &lt;code&gt;false&lt;/code&gt; for the respective inputs&lt;/td&gt;
&lt;td&gt;verified in sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;validateAll&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fun validateAll(obj: Object, rules: Object): Object = do {&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;returns &lt;code&gt;valid&lt;/code&gt; as &lt;code&gt;false&lt;/code&gt; and &lt;code&gt;errors&lt;/code&gt; as &lt;code&gt;[{ valid: false, field: "email", error: "email is required" }]&lt;/code&gt; when rules fail&lt;/td&gt;
&lt;td&gt;verified in sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;hasRequiredFields&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;fun hasRequiredFields(obj: Object, fields: Array&amp;lt;String&amp;gt;): Object = do {&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;returns &lt;code&gt;valid&lt;/code&gt; as &lt;code&gt;false&lt;/code&gt;, &lt;code&gt;missing&lt;/code&gt; as &lt;code&gt;["email", "phone"]&lt;/code&gt;, and &lt;code&gt;error&lt;/code&gt; as &lt;code&gt;"Missing required fields: email, phone"&lt;/code&gt; when fields are absent&lt;/td&gt;
&lt;td&gt;verified in sandbox&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Falpa3jgozsjqv9w2a51a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Falpa3jgozsjqv9w2a51a.png" alt="flow of the transform" width="800" height="816"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How to read it
&lt;/h2&gt;

&lt;p&gt;I check the Function column for the exact module export name and verify the Signature column matches my runtime DataWeave version. I read the Returns / behaviour column to confirm whether the call yields a Boolean or an Object containing valid, field, and error keys, noting that minLength defaults the field name to "field". I always place import modules::ValidationUtils at the top of my script before referencing any exported function.&lt;/p&gt;

&lt;h2&gt;
  
  
  Traps
&lt;/h2&gt;

&lt;p&gt;Missing import causes the runtime to fail with Unable to resolve reference of &lt;code&gt;ValidationUtils::isRequired&lt;/code&gt; when the script executes without the module declaration.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I do now
&lt;/h2&gt;

&lt;p&gt;I load the module at the top of every DataWeave file and pass payload fields directly into the validation functions or wrap them in validateAll for bulk checks. I parse the returned Object to extract the valid flag and route flows based on whether errors contains entries.&lt;/p&gt;

&lt;p&gt;Runs shown: DataWeave CLI 2.12.2&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/shakarbisetty/mulesoft-cookbook/tree/main/dataweave/modules/dw-validation-utils" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F508bxfch9tb6wpumw99b.png" alt="MuleSoft patterns, proven and runnable" width="800" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;104 DataWeave patterns + 8 Exchange modules with 208 MUnit tests: github.com/shakarbisetty/mulesoft-cookbook | 60-second walkthroughs: youtube.com/@SanThaParv&lt;/p&gt;

</description>
      <category>validation</category>
      <category>dataweave</category>
      <category>modules</category>
      <category>reference</category>
    </item>
    <item>
      <title>RFC 7807 Error Envelope with DataWeave Try Function</title>
      <dc:creator>Shakar Bisetty</dc:creator>
      <pubDate>Wed, 09 Sep 2026 15:16:31 +0000</pubDate>
      <link>https://dev.to/thasha/rfc-7807-error-envelope-with-dataweave-try-function-10g0</link>
      <guid>https://dev.to/thasha/rfc-7807-error-envelope-with-dataweave-try-function-10g0</guid>
      <description>&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;My script failed when &lt;code&gt;try&lt;/code&gt; lacked an explicit import from &lt;code&gt;dw::Runtime&lt;/code&gt;. The runtime reported &lt;code&gt;Unable to resolve reference of: try&lt;/code&gt; during execution. The documentation describes &lt;code&gt;try&lt;/code&gt; as a function that returns a Result object for handling errors &lt;a href="https://docs.mulesoft.com/dataweave/latest/dw-runtime-functions-try" rel="noopener noreferrer"&gt;docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Input
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"order"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORD-2001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"12.5O"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsm9xfaex1scnd4kbtb01.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsm9xfaex1scnd4kbtb01.png" alt="flow of the transform" width="799" height="131"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Working configuration
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%dw 2.0
// try() lives in dw::Runtime — not imported by default
import try from dw::Runtime
// RFC 7807 media type is accepted as-is by the JSON writer
output application/problem+json
var attempt = try(() -&amp;gt; payload.order.total as Number)
---
if (attempt.success) { total: attempt.result }
else {
  "type": "https://example.com/problems/invalid-total",
  "title": "Invalid order total",
  "status": 400,
  "detail": attempt.error.message,
  "instance": "/orders/" ++ payload.order.id,
  "errorKind": attempt.error.kind
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Output
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://example.com/problems/invalid-total"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Invalid order total"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;400&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Cannot coerce String (12.5O) to Number"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"instance"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/orders/ORD-2001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"errorKind"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"InvalidNumberException"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;detail&lt;/code&gt; field contains the string &lt;code&gt;Cannot coerce String (12.5O) to Number&lt;/code&gt;. The &lt;code&gt;errorKind&lt;/code&gt; field holds the value &lt;code&gt;InvalidNumberException&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trap
&lt;/h2&gt;

&lt;p&gt;The missing import causes a resolution failure when the script executes. Running &lt;code&gt;fail_noimport.dwl&lt;/code&gt; produces an exit code of 255 and reports &lt;code&gt;Unable to resolve reference of: try&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I do now
&lt;/h2&gt;

&lt;p&gt;I always add &lt;code&gt;import try from dw::Runtime&lt;/code&gt; at the top of every script that uses the function. I verify the import exists before running the transformation to prevent resolution errors.&lt;/p&gt;

&lt;p&gt;Runs shown: DataWeave CLI 2.12.2&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/shakarbisetty/mulesoft-cookbook/tree/main/dataweave/patterns/07-error-handling/rfc-7807-error-envelope-in-dataweave" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F508bxfch9tb6wpumw99b.png" alt="MuleSoft patterns, proven and runnable" width="800" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;104 DataWeave patterns + 8 Exchange modules with 208 MUnit tests: github.com/shakarbisetty/mulesoft-cookbook | 60-second walkthroughs: youtube.com/@SanThaParv&lt;/p&gt;

</description>
      <category>rfc7807</category>
      <category>dataweave</category>
      <category>try</category>
      <category>errors</category>
    </item>
    <item>
      <title>Partition bulk records into accepted and rejected lists</title>
      <dc:creator>Shakar Bisetty</dc:creator>
      <pubDate>Wed, 09 Sep 2026 15:13:29 +0000</pubDate>
      <link>https://dev.to/thasha/partition-bulk-records-into-accepted-and-rejected-lists-2e5</link>
      <guid>https://dev.to/thasha/partition-bulk-records-into-accepted-and-rejected-lists-2e5</guid>
      <description>&lt;h2&gt;
  
  
  Problem
&lt;/h2&gt;

&lt;p&gt;My DataWeave script threw an unresolved reference error when calling &lt;code&gt;partition&lt;/code&gt; without an explicit import. The Arrays module must be imported to your DataWeave code by adding the line &lt;code&gt;import * from dw::core::Arrays&lt;/code&gt; to the header of your DataWeave script &lt;a href="https://docs.mulesoft.com/dataweave/latest/dw-arrays" rel="noopener noreferrer"&gt;docs&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Input
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"records"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORD-1001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ana@example.com"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORD-1002"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bad-address"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORD-1003"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"raj@example.org"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORD-1004"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORD-1005"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mei@example.net"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvl34ge1bn7uz8702u427.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvl34ge1bn7uz8702u427.png" alt="flow of the transform" width="799" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Working configuration
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%dw 2.0
// partition() lives in dw::core::Arrays — not imported by default
import * from dw::core::Arrays
output application/json
// keys are ALWAYS success/failure — re-map them to domain names
var split = payload.records partition (r) -&amp;gt;
    (r.email default "") matches /.+@.+\..+/
---
{
  accepted: split.success map (r) -&amp;gt; r.id,
  rejected: split.failure map (r) -&amp;gt; {
    id: r.id,
    reason: "missing or invalid email"
  },
  retryCount: sizeOf(split.failure)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Output
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"accepted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"ORD-1001"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"ORD-1003"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"ORD-1005"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"rejected"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORD-1002"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"missing or invalid email"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ORD-1004"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"missing or invalid email"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"retryCount"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;accepted&lt;/code&gt; array contains IDs from records with valid emails, while the &lt;code&gt;rejected&lt;/code&gt; array holds objects for records missing or invalidating email addresses. The &lt;code&gt;retryCount&lt;/code&gt; field reports the size of the failure partition as two.&lt;/p&gt;

&lt;h2&gt;
  
  
  The trap
&lt;/h2&gt;

&lt;p&gt;The trap is an unresolved reference error when calling &lt;code&gt;partition&lt;/code&gt; without importing the Arrays module.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I do now
&lt;/h2&gt;

&lt;p&gt;I add &lt;code&gt;import * from dw::core::Arrays&lt;/code&gt; to the DataWeave header before using &lt;code&gt;partition&lt;/code&gt;. I map the &lt;code&gt;success&lt;/code&gt; and &lt;code&gt;failure&lt;/code&gt; keys to domain names like &lt;code&gt;accepted&lt;/code&gt; and &lt;code&gt;rejected&lt;/code&gt;. I calculate retry counts using &lt;code&gt;sizeOf&lt;/code&gt; on the failure partition.&lt;/p&gt;

&lt;p&gt;Runs shown: DataWeave CLI 2.12.2&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/shakarbisetty/mulesoft-cookbook/tree/main/dataweave/patterns/01-array-manipulation/dataweave-partition-for-bulk-api-responses" rel="noopener noreferrer"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F508bxfch9tb6wpumw99b.png" alt="MuleSoft patterns, proven and runnable" width="800" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;104 DataWeave patterns + 8 Exchange modules with 208 MUnit tests: github.com/shakarbisetty/mulesoft-cookbook | 60-second walkthroughs: youtube.com/@SanThaParv&lt;/p&gt;

</description>
      <category>partition</category>
      <category>dataweave</category>
      <category>arrays</category>
      <category>validation</category>
    </item>
    <item>
      <title>RFC 7807 Error Responses in DataWeave: One Transform for All 14 Endpoints</title>
      <dc:creator>Shakar Bisetty</dc:creator>
      <pubDate>Sun, 12 Apr 2026 05:42:27 +0000</pubDate>
      <link>https://dev.to/thasha/rfc-7807-error-responses-in-dataweave-one-transform-for-all-14-endpoints-4el0</link>
      <guid>https://dev.to/thasha/rfc-7807-error-responses-in-dataweave-one-transform-for-all-14-endpoints-4el0</guid>
      <description>&lt;p&gt;Our API had 14 endpoints and 14 different error response formats. The mobile team spent 3 days writing separate error parsers for each one. I standardized everything to RFC 7807 with one DataWeave transform.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RFC 7807 defines a standard error format: type, title, status, detail, timestamp&lt;/li&gt;
&lt;li&gt;One DataWeave error mapping lookup replaces 14 inconsistent formats&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;default&lt;/code&gt; clause on the lookup is critical — unmapped errors return null without it&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;application/problem+json&lt;/code&gt; MIME type supported since Mule 4.4&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Problem: 14 Endpoints, 14 Error Formats
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Endpoint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;A&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"not found"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Endpoint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;B&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Resource missing"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"code"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Endpoint&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;C&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"errors"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="nl"&gt;"field"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"issue"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"invalid"&lt;/span&gt;&lt;span class="p"&gt;}]}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The mobile team needed a different error parser for each endpoint. 3 days of defensive parsing just to show error messages.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: RFC 7807 Error Envelope
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%dw 2.0
output application/json
var errorMapping = {
    "HTTP:BAD_REQUEST": { status: 400, title: "Bad Request", "type": "client-error" },
    "HTTP:UNAUTHORIZED": { status: 401, title: "Unauthorized", "type": "authentication" },
    "HTTP:NOT_FOUND": { status: 404, title: "Not Found", "type": "not-found" },
    "HTTP:TIMEOUT": { status: 504, title: "Gateway Timeout", "type": "timeout" }
}
var errorInfo = errorMapping[payload.error.errorType.identifier] default { status: 500, title: "Internal Server Error", "type": "server-error" }
---
{
  "type": "https://api.example.com/errors/$(errorInfo.'type')",
  title: "errorInfo.title,"
  status: errorInfo.status,
  detail: payload.error.description,
  timestamp: now()
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every error, every endpoint, same format. The mobile team wrote 1 parser.&lt;/p&gt;




&lt;p&gt;100 production-ready DataWeave patterns with tests: &lt;a href="https://github.com/shakarbisetty/mulesoft-cookbook" rel="noopener noreferrer"&gt;mulesoft-cookbook on GitHub&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Error Mapping Lookup
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;errorMapping&lt;/code&gt; object maps Mule error type identifiers to HTTP semantics:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var errorInfo = errorMapping[payload.error.errorType.identifier]
  default { status: 500, title: "Internal Server Error", "type": "server-error" }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;errorType.identifier&lt;/code&gt; comes from Mule's error object — values like "HTTP:BAD_REQUEST", "HTTP:NOT_FOUND", "VALIDATION:INVALID_PAYLOAD".&lt;/p&gt;

&lt;p&gt;The lookup &lt;code&gt;errorMapping["HTTP:NOT_FOUND"]&lt;/code&gt; returns the mapped status and title. If the error type isn't in the mapping, &lt;code&gt;default&lt;/code&gt; provides a 500 fallback.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trap: Missing default Clause
&lt;/h2&gt;

&lt;p&gt;Without &lt;code&gt;default&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var errorInfo = errorMapping[payload.error.errorType.identifier]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An unmapped error type (like "CUSTOM:BUSINESS_RULE") returns &lt;code&gt;null&lt;/code&gt;. Your response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"detail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Business rule violated"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Technically valid JSON. Completely useless to the client. The mobile app shows "null" as the error title.&lt;/p&gt;

&lt;p&gt;I hit this in production when a custom connector threw "CUSTOM:RATE_EXCEEDED" — an error type I hadn't mapped. The API returned a null-filled error response. The mobile app crashed trying to display &lt;code&gt;null.title&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; Always use &lt;code&gt;default&lt;/code&gt; on error mapping lookups. The default should be a valid 500 response, not null.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Reserved Word Trap
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;errorInfo.'type'   // Works — quoted field access
errorInfo.type     // Parse error — "type" is reserved in DataWeave
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;type&lt;/code&gt; is a reserved keyword. When your error mapping uses &lt;code&gt;type&lt;/code&gt; as a field name, you must access it with quotes: &lt;code&gt;errorInfo.'type'&lt;/code&gt;. Without quotes, DataWeave throws a parse error.&lt;/p&gt;

&lt;p&gt;I spent 20 minutes on this. The error message says "unexpected token" — it doesn't mention that &lt;code&gt;type&lt;/code&gt; is reserved.&lt;/p&gt;

&lt;h2&gt;
  
  
  RFC 7807 Fields
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Required&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;type&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;URI identifying the error category&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;title&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Human-readable error title&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;status&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;HTTP status code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;detail&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Specific error description&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;instance&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;URI of the failing request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;timestamp&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;When the error occurred&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;correlationId&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Request trace ID&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  What I Do Now
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Every API project gets the error envelope pattern in the global error handler&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;default&lt;/code&gt; on every mapping lookup — no exceptions&lt;/li&gt;
&lt;li&gt;Quoted access for &lt;code&gt;type&lt;/code&gt; field&lt;/li&gt;
&lt;li&gt;I add &lt;code&gt;correlationId&lt;/code&gt; from the Mule correlation ID for trace support&lt;/li&gt;
&lt;li&gt;Content-Type: &lt;code&gt;application/problem+json&lt;/code&gt; (supported since Mule 4.4)&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;100 patterns with MUnit tests: &lt;a href="https://github.com/shakarbisetty/mulesoft-cookbook" rel="noopener noreferrer"&gt;github.com/shakarbisetty/mulesoft-cookbook&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;60-second video walkthroughs: &lt;a href="https://www.youtube.com/@SanThaParv?sub_confirmation=1" rel="noopener noreferrer"&gt;youtube.com/@SanThaParv&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dataweave</category>
      <category>mulesoft</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>DataWeave partition() for Bulk API Responses: Stop Returning Silent 200 OKs</title>
      <dc:creator>Shakar Bisetty</dc:creator>
      <pubDate>Sun, 05 Apr 2026 21:26:44 +0000</pubDate>
      <link>https://dev.to/thasha/dataweave-partition-for-bulk-api-responses-stop-returning-silent-200-oks-44eg</link>
      <guid>https://dev.to/thasha/dataweave-partition-for-bulk-api-responses-stop-returning-silent-200-oks-44eg</guid>
      <description>&lt;p&gt;Our bulk import API returned 200 OK on every request. Nobody noticed that 40% of records were failing silently for 3 days.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;partition()&lt;/code&gt; from &lt;code&gt;dw::core::Arrays&lt;/code&gt; splits an array into success/failure groups in one pass&lt;/li&gt;
&lt;li&gt;Build summary (total, succeeded, failed, successRate) + per-record results + error extraction&lt;/li&gt;
&lt;li&gt;Empty batch trap: &lt;code&gt;successCount / total * 100&lt;/code&gt; divides by zero on empty arrays&lt;/li&gt;
&lt;li&gt;The caller needs to know EXACTLY what failed and why — not just "200 OK"&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Problem: Silent Bulk Failures
&lt;/h2&gt;

&lt;p&gt;We had a bulk import API. Clients sent 5,000-10,000 records per batch. The API processed each record individually — some succeeded, some failed (duplicate keys, invalid formats, missing fields).&lt;/p&gt;

&lt;p&gt;The old response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Batch processed"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. No per-record status. No failure count. No error details. The client had no idea which records failed. They found out 3 days later when reconciliation showed missing data.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: partition() + Summary Builder
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%dw 2.0
import partition from dw::core::Arrays
output application/json
var parts = payload partition (item) -&amp;gt; item.status == "SUCCESS"
var successCount = sizeOf(parts.success)
var total = sizeOf(payload)
---
{
  summary: {
    total: total,
    successful: successCount,
    failed: total - successCount,
    successRate: (successCount / total * 100) as String ++ "%"
  },
  results: payload map {
    id: $.id,
    status: $.status,
    (error: $.error) if $.status == "FAILED"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;100 production-ready DataWeave patterns with tests: &lt;a href="https://github.com/shakarbisetty/mulesoft-cookbook" rel="noopener noreferrer"&gt;mulesoft-cookbook on GitHub&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How partition() Works
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;partition()&lt;/code&gt; takes an array and a predicate. Returns an object with two keys:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;success&lt;/code&gt;: all items where the predicate returned true&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;failure&lt;/code&gt;: all items where it returned false
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import partition from dw::core::Arrays
---
[1, 2, 3, 4, 5] partition (n) -&amp;gt; n &amp;gt; 3
// {success: [4, 5], failure: [1, 2, 3]}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One pass. No need to filter twice. Cleaner than separate &lt;code&gt;filter&lt;/code&gt; calls for success and failure.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Conditional Key Trick
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;results: payload map {
  id: $.id,
  status: $.status,
  (error: $.error) if $.status == "FAILED"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;(error: $.error) if $.status == "FAILED"&lt;/code&gt; syntax conditionally includes the error field. Successful records have no &lt;code&gt;error&lt;/code&gt; key — it's absent, not null. Clean API response.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trap: Divide by Zero on Empty Batch
&lt;/h2&gt;

&lt;p&gt;A health check sent an empty array to our bulk endpoint. &lt;code&gt;total = 0&lt;/code&gt;. The successRate calculation: &lt;code&gt;0 / 0 * 100&lt;/code&gt;. Runtime error: "Cannot divide by zero."&lt;/p&gt;

&lt;p&gt;CloudHub showed a 500 error. The health check saw "API is down." The API was fine — it just crashed on empty input.&lt;/p&gt;

&lt;p&gt;The fix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;successRate: if (total &amp;gt; 0) ((successCount / total * 100) as String ++ "%") else "N/A"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One guard. I now add this to every bulk response builder. Empty batches are valid input — your API should handle them gracefully.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Caller Gets
&lt;/h2&gt;

&lt;p&gt;Before (useless):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"OK"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Batch processed"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After (actionable):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"summary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"total"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"successful"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"failed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"successRate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"60.0%"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"results"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"R1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SUCCESS"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"R2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FAILED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Duplicate key"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"R3"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SUCCESS"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"R4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SUCCESS"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"R5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FAILED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Invalid format"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The caller knows: 60% success rate. R2 failed on duplicate key. R5 failed on format. They can retry just the 2 failures instead of resending all 5.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Do Now
&lt;/h2&gt;

&lt;p&gt;Every bulk API I build includes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;partition()&lt;/code&gt; for success/failure split&lt;/li&gt;
&lt;li&gt;Summary with counts + percentage&lt;/li&gt;
&lt;li&gt;Per-record results with conditional error field&lt;/li&gt;
&lt;li&gt;Empty-batch guard on all division operations&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;errors&lt;/code&gt; array as a convenience extraction for clients that only want failures&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;100 patterns with MUnit tests: &lt;a href="https://github.com/shakarbisetty/mulesoft-cookbook" rel="noopener noreferrer"&gt;github.com/shakarbisetty/mulesoft-cookbook&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;60-second video walkthroughs: &lt;a href="https://www.youtube.com/@SanThaParv?sub_confirmation=1" rel="noopener noreferrer"&gt;youtube.com/@SanThaParv&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dataweave</category>
      <category>mulesoft</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Parsing LLM Responses in DataWeave: 3 Layers of Defense Against Markdown Fences</title>
      <dc:creator>Shakar Bisetty</dc:creator>
      <pubDate>Sun, 05 Apr 2026 20:12:23 +0000</pubDate>
      <link>https://dev.to/thasha/parsing-llm-responses-in-dataweave-3-layers-of-defense-against-markdown-fences-4ch5</link>
      <guid>https://dev.to/thasha/parsing-llm-responses-in-dataweave-3-layers-of-defense-against-markdown-fences-4ch5</guid>
      <description>&lt;p&gt;I connected MuleSoft to GPT-4o last quarter for a support ticket classifier. The prompt builder worked (I covered that in a previous post). The LLM call worked. Then the response came back and my parser crashed.&lt;/p&gt;

&lt;p&gt;The LLM was supposed to return clean JSON. Instead it returned: "Here is my analysis:\n&lt;br&gt;
&lt;br&gt;
&lt;code&gt;json\n{\"ranking\": [\"TK-101\"]}\n&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
\nLet me know if you need anything else."&lt;/p&gt;

&lt;p&gt;Markdown fences. Preamble text. Trailing conversation. My &lt;code&gt;read()&lt;/code&gt; call choked on "Here is my analysis" — not valid JSON.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;LLMs wrap JSON responses in markdown fences — your parser must extract the JSON first&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;try()&lt;/code&gt; from &lt;code&gt;dw::Runtime&lt;/code&gt; catches parse failures gracefully — without it, one malformed response crashes your flow&lt;/li&gt;
&lt;li&gt;Always validate required keys after parsing — LLMs hallucinate extra fields and omit required ones&lt;/li&gt;
&lt;li&gt;The regex for fence extraction: &lt;code&gt;raw match /(?s)&lt;/code&gt;&lt;code&gt;(?:json)?\s*(\{.*?\})\s*&lt;/code&gt;&lt;code&gt;/&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Test with 5 response variants: clean JSON, fenced JSON, fenced without language tag, no fences, broken JSON&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  The Problem: LLMs Don't Return Clean JSON
&lt;/h2&gt;

&lt;p&gt;You ask GPT-4o to return JSON. Sometimes it does:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"ranking"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"TK-101"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"TK-098"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"summary"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Timeout is critical."&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But often it adds commentary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;Here&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;my&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;analysis:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
json&lt;br&gt;
{"ranking": ["TK-101"], "summary": "Timeout is critical."}&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Let me know if you need anything else.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
plaintext&lt;/p&gt;

&lt;p&gt;That's not valid JSON. &lt;code&gt;read(raw, "application/json")&lt;/code&gt; throws. Your Mule flow crashes. 50,000 LLM responses per day = 50,000 potential crashes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: 3-Layer Defense in DataWeave
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%dw 2.0
import try from dw::Runtime
output application/json
var raw = payload.rawResponse
var fenceMatch = raw match /(?s)```

(?:json)?\s*(\{.*?\})\s*

```/
var jsonStr = if (fenceMatch[1]?) fenceMatch[1] else raw
var parsed = try(() -&amp;gt; read(jsonStr, "application/json"))
var keys = if (parsed.success) (parsed.result pluck $$) else []
var missing = payload.requiredKeys filter (k) -&amp;gt; !(keys contains k)
---
{
  parsed: if (parsed.success) parsed.result else null,
  valid: parsed.success and isEmpty(missing),
  missingKeys: missing
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;100 production-ready DataWeave patterns with tests: &lt;a href="https://github.com/shakarbisetty/mulesoft-cookbook" rel="noopener noreferrer"&gt;mulesoft-cookbook on GitHub&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Layer 1: Regex Fence Extraction
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var fenceMatch = raw match /(?s)```

(?:json)?\s*(\{.*?\})\s*

```/
var jsonStr = if (fenceMatch[1]?) fenceMatch[1] else raw
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The regex matches content between markdown fences. &lt;code&gt;(?s)&lt;/code&gt; enables dotall mode so &lt;code&gt;.&lt;/code&gt; matches newlines. &lt;code&gt;(?:json)?&lt;/code&gt; handles both&lt;br&gt;
&lt;br&gt;
 &lt;code&gt;json` and bare&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
`&lt;code&gt;. The captured group&lt;/code&gt;({.*?})` extracts just the JSON object.&lt;/p&gt;

&lt;p&gt;If no fences found (&lt;code&gt;fenceMatch[1]?&lt;/code&gt; is false), fall back to parsing the raw string directly. This handles the case where the LLM returns clean JSON without fences.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 2: try() Wraps the Parse
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;`dataweave&lt;br&gt;
var parsed = try(() -&amp;gt; read(jsonStr, "application/json"))&lt;br&gt;
`&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;try()&lt;/code&gt; from &lt;code&gt;dw::Runtime&lt;/code&gt; is the critical piece. Without it, &lt;code&gt;read()&lt;/code&gt; on invalid JSON throws an exception and crashes the flow. With &lt;code&gt;try()&lt;/code&gt;, you get:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Success: &lt;code&gt;{success: true, result: {...parsed object...}}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Failure: &lt;code&gt;{success: false, error: {...error details...}}&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Your flow continues either way. Log the error, route to a dead-letter queue, retry with a different prompt — your choice. But the flow never crashes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The trap:&lt;/strong&gt; If you don't check &lt;code&gt;parsed.success&lt;/code&gt; before accessing &lt;code&gt;parsed.result&lt;/code&gt;, you get &lt;code&gt;null&lt;/code&gt; on failure. Downstream code that expects an object gets null instead. Always check the success flag.&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 3: Required Key Validation
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;`dataweave&lt;br&gt;
var keys = if (parsed.success) (parsed.result pluck $$) else []&lt;br&gt;
var missing = payload.requiredKeys filter (k) -&amp;gt; !(keys contains k)&lt;br&gt;
`&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;LLMs hallucinate. You asked for &lt;code&gt;ranking&lt;/code&gt; and &lt;code&gt;summary&lt;/code&gt;. The LLM returned &lt;code&gt;ranking&lt;/code&gt;, &lt;code&gt;analysis&lt;/code&gt;, and &lt;code&gt;confidence&lt;/code&gt; — but not &lt;code&gt;summary&lt;/code&gt;. The JSON is valid but missing a required field.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pluck $$&lt;/code&gt; extracts all keys from the parsed object. &lt;code&gt;filter&lt;/code&gt; checks each required key against the actual keys. &lt;code&gt;missing&lt;/code&gt; tells you exactly which keys the LLM omitted.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 5 Response Variants to Test
&lt;/h2&gt;

&lt;p&gt;I test every LLM parser against these variants:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Variant&lt;/th&gt;
&lt;th&gt;Example&lt;/th&gt;
&lt;th&gt;What Breaks&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Clean JSON&lt;/td&gt;
&lt;td&gt;&lt;code&gt;{"ranking": [...], "summary": "..."}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Nothing — baseline&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fenced with language&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;&lt;/code&gt;json\n{...}\n&lt;code&gt;&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;read()&lt;/code&gt; without fence extraction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fenced without language&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;&lt;/code&gt;\n{...}\n&lt;code&gt;&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Regex that requires &lt;code&gt;json&lt;/code&gt; after fence&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No fences, with preamble&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Here is my analysis: {"ranking": [...]}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Naive &lt;code&gt;read()&lt;/code&gt; on full string&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Broken JSON&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;{"ranking": ["TK-101"&lt;/code&gt; (truncated)&lt;/td&gt;
&lt;td&gt;Any parser without &lt;code&gt;try()&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I hit all 5 in production within the first week. The same model, same prompt, same temperature — 5 different formats across 50,000 responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trap: The Regex Doesn't Handle Nested Objects Well
&lt;/h2&gt;

&lt;p&gt;The regex &lt;code&gt;\{.*?\}&lt;/code&gt; uses non-greedy matching. It works for flat JSON objects. But if your LLM returns nested objects:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;`json&lt;br&gt;
{"analyses": [{"ticketId": "TK-101", "action": "increase pool"}], "summary": "..."}&lt;br&gt;
`&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;The non-greedy &lt;code&gt;.*?&lt;/code&gt; stops at the first &lt;code&gt;}&lt;/code&gt; — inside the nested array. You get a partial JSON parse.&lt;/p&gt;

&lt;p&gt;For production, I switched to finding the matching closing brace by counting open/close braces, not regex. But the regex works for 90% of LLM responses where the top-level structure is flat.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;p&gt;The parser handles 50,000 LLM responses per day. Average parse time: 2ms per response. The regex is the slowest part — &lt;code&gt;try()&lt;/code&gt; and &lt;code&gt;read()&lt;/code&gt; are fast because they're native DataWeave functions.&lt;/p&gt;

&lt;p&gt;Zero flow crashes since deploying the 3-layer approach. Before it, we had 3-5 crashes per day from malformed LLM responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Do Now
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Every LLM integration gets this parser as the first step after the API call&lt;/li&gt;
&lt;li&gt;Failed parses go to a dead-letter queue with the raw response for debugging&lt;/li&gt;
&lt;li&gt;Missing key reports go to a monitoring dashboard — tracks LLM reliability over time&lt;/li&gt;
&lt;li&gt;I test with all 5 variants before any production deployment&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;100 patterns with MUnit tests: &lt;a href="https://github.com/shakarbisetty/mulesoft-cookbook" rel="noopener noreferrer"&gt;github.com/shakarbisetty/mulesoft-cookbook&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;60-second video walkthroughs: &lt;a href="https://www.youtube.com/@SanThaParv?sub_confirmation=1" rel="noopener noreferrer"&gt;youtube.com/@SanThaParv&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dataweave</category>
      <category>mulesoft</category>
      <category>ai</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building LLM Prompts From Enterprise Data in DataWeave: 2 Traps That Garbled My AI Output</title>
      <dc:creator>Shakar Bisetty</dc:creator>
      <pubDate>Sun, 05 Apr 2026 18:53:50 +0000</pubDate>
      <link>https://dev.to/thasha/building-llm-prompts-from-enterprise-data-in-dataweave-2-traps-that-garbled-my-ai-output-41f8</link>
      <guid>https://dev.to/thasha/building-llm-prompts-from-enterprise-data-in-dataweave-2-traps-that-garbled-my-ai-output-41f8</guid>
      <description>&lt;p&gt;I connected a MuleSoft API to an LLM last quarter for a support ticket classifier. The API call was easy — the MuleSoft AI Connector handles that. Building the prompt payload from enterprise data? That's where I spent 2 hours debugging escape sequences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DataWeave transforms ticket data into structured LLM prompt payloads (system + user roles)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;joinBy "\n"&lt;/code&gt; produces literal backslash-n in JSON — not actual newlines. The LLM sees one continuous line.&lt;/li&gt;
&lt;li&gt;No token estimation → prompt consumes most of the context window → truncated response&lt;/li&gt;
&lt;li&gt;The pattern builds system role, user role, model config, and structured response format in 12 lines&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Pattern: Enterprise Data to LLM Prompt
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%dw 2.0
output application/json
var systemPrompt = "You are an enterprise support analyst."
var lines = payload.ticketHistory map (t) -&amp;gt; "- [$(t.priority upper)] $(t.id): $(t.subject)"
var userPrompt = "Analyze tickets for $(payload.customer.name):\n" ++ (lines joinBy "\n")
---
{
  model: payload.model,
  max_tokens: payload.maxTokens,
  messages: [
    {role: "system", content: systemPrompt},
    {role: "user", content: userPrompt}
  ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Input: customer object + ticket array + model config.&lt;br&gt;
Output: ready-to-send LLM payload with system instructions and contextual user prompt.&lt;/p&gt;



&lt;p&gt;100 production-ready DataWeave patterns with tests: &lt;a href="https://github.com/shakarbisetty/mulesoft-cookbook" rel="noopener noreferrer"&gt;mulesoft-cookbook on GitHub&lt;/a&gt;&lt;/p&gt;


&lt;h2&gt;
  
  
  Trap 1: joinBy "\n" Is Literal, Not a Newline
&lt;/h2&gt;

&lt;p&gt;The prompt looks correct in the DataWeave Playground:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- [HIGH] TK-101: API timeout
- [MEDIUM] TK-098: OAuth refresh failing
- [LOW] TK-095: Batch stuck at 80 pct
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But the actual JSON sent to the LLM contains:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"content"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"- [HIGH] TK-101: API timeout&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;- [MEDIUM] TK-098: OAuth refresh failing&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;- [LOW] TK-095: Batch stuck at 80 pct"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Literal &lt;code&gt;\n&lt;/code&gt; characters. Not newlines. The LLM sees one continuous string and its analysis is garbled — it can't distinguish between tickets.&lt;/p&gt;

&lt;p&gt;I spent 2 hours wondering why the classification was wrong before I checked the raw HTTP request body.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Use an actual newline in a variable, or use template literals that preserve whitespace.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trap 2: No Token Estimation
&lt;/h2&gt;

&lt;p&gt;I injected 200 ticket summaries into one prompt. Each summary is ~20 tokens. That's 4,000 tokens just for the ticket list. max_tokens was set to 500 for the response.&lt;/p&gt;

&lt;p&gt;The model's context window was 4,096 tokens. Prompt + response budget = 4,500 tokens. Didn't fit. The response was truncated mid-sentence.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The fix:&lt;/strong&gt; Estimate prompt tokens before setting max_tokens:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var estimatedPromptTokens = sizeOf(userPrompt) / 4
var safeMaxTokens = 4096 - estimatedPromptTokens - 100
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  When to Use This Pattern
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use it when&lt;/th&gt;
&lt;th&gt;Alternatives&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MuleSoft AI Connector integration&lt;/td&gt;
&lt;td&gt;Direct API call with HTTP requester&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Structured enterprise data → LLM prompt&lt;/td&gt;
&lt;td&gt;Hardcoded prompts (won't scale)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Dynamic context injection (tickets, customer data)&lt;/td&gt;
&lt;td&gt;Static system prompts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Multiple LLM providers (swap model field)&lt;/td&gt;
&lt;td&gt;Provider-specific SDK&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;p&gt;100 patterns with MUnit tests: &lt;a href="https://github.com/shakarbisetty/mulesoft-cookbook" rel="noopener noreferrer"&gt;github.com/shakarbisetty/mulesoft-cookbook&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;60-second video walkthroughs: &lt;a href="https://www.youtube.com/@SanThaParv?sub_confirmation=1" rel="noopener noreferrer"&gt;youtube.com/@SanThaParv&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dataweave</category>
      <category>mulesoft</category>
      <category>ai</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>DataWeave Runtime Type Detection: Auto-Discover Schema Mismatches Before They Break Production</title>
      <dc:creator>Shakar Bisetty</dc:creator>
      <pubDate>Sun, 05 Apr 2026 17:37:34 +0000</pubDate>
      <link>https://dev.to/thasha/dataweave-runtime-type-detection-auto-discover-schema-mismatches-before-they-break-production-3768</link>
      <guid>https://dev.to/thasha/dataweave-runtime-type-detection-auto-discover-schema-mismatches-before-they-break-production-3768</guid>
      <description>&lt;p&gt;I built a schema discovery function for a 6-system integration last year. Each source sent different types for the same fields. One function auto-detects types at runtime and builds a complete schema.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;describeType&lt;/code&gt; function uses &lt;code&gt;is&lt;/code&gt; operator chain to detect String, Number, Boolean, Array, Object, Null at runtime&lt;/li&gt;
&lt;li&gt;Run against sample records from each source to discover type mismatches before writing transforms&lt;/li&gt;
&lt;li&gt;Order of &lt;code&gt;is&lt;/code&gt; checks matters — always check Array before Object&lt;/li&gt;
&lt;li&gt;Null handling needs explicit check — don't rely on catch-all else&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Problem: 6 Sources, 6 Different Types
&lt;/h2&gt;

&lt;p&gt;We integrated with 6 systems. All sent the same logical data — customer fields. But the actual types varied:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Source A&lt;/th&gt;
&lt;th&gt;Source B&lt;/th&gt;
&lt;th&gt;Source C&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;age&lt;/td&gt;
&lt;td&gt;Number &lt;code&gt;30&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;String &lt;code&gt;"30"&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;active&lt;/td&gt;
&lt;td&gt;Boolean &lt;code&gt;true&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;String &lt;code&gt;"true"&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Number &lt;code&gt;1&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;tags&lt;/td&gt;
&lt;td&gt;Array &lt;code&gt;["admin"]&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;String &lt;code&gt;"admin"&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;null&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Writing transforms that assumed consistent types would silently produce wrong output.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Solution: Runtime Type Detection
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%dw 2.0
output application/json
fun describeType(value) =
  if (value is String) "String"
  else if (value is Number) "Number"
  else if (value is Boolean) "Boolean"
  else if (value is Array) "Array"
  else if (value is Object) "Object"
  else if (value is Null) "Null"
  else "Unknown"
---
{schema: payload.fields map (f) -&amp;gt; ({
  key: f.key,
  type: describeType(f.value),
  nullable: f.value is Null,
  (example: f.value) if (f.value is String or f.value is Number),
  (itemCount: sizeOf(f.value)) if (f.value is Array)
})}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Feed it any payload. Get back a schema with field names, detected types, nullability, and examples.&lt;/p&gt;




&lt;p&gt;100 production-ready DataWeave patterns with tests: &lt;a href="https://github.com/shakarbisetty/mulesoft-cookbook" rel="noopener noreferrer"&gt;mulesoft-cookbook on GitHub&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Trap 1: Order of &lt;code&gt;is&lt;/code&gt; Checks Matters
&lt;/h2&gt;

&lt;p&gt;Check Array BEFORE Object. In some DataWeave versions, &lt;code&gt;[] is Object&lt;/code&gt; can return true before the Array check runs. Your schema shows "Object" for fields that are actually Arrays.&lt;/p&gt;

&lt;p&gt;I hit this in production — an array field got classified as Object. The downstream parser expected an Array, got the string "Object", and silently skipped the field. 200 records processed with missing tags.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule:&lt;/strong&gt; Most specific types first: Null → Boolean → Number → String → Array → Object → Unknown.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trap 2: Null Falls Through
&lt;/h2&gt;

&lt;p&gt;If your catch-all &lt;code&gt;else&lt;/code&gt; returns "Unknown", null values get classified as "Unknown" instead of "Null". Always check &lt;code&gt;is Null&lt;/code&gt; explicitly — don't rely on the else clause.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Preflight Process
&lt;/h2&gt;

&lt;p&gt;I run this on every new integration now:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Sample 100 records&lt;/strong&gt; from each source system&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Run describeType&lt;/strong&gt; on every field&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compare schemas&lt;/strong&gt; across sources — flag any field where types differ&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Write transforms&lt;/strong&gt; with explicit type handling for mismatched fields&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On the 6-system project, this found 4 fields with type mismatches across 2 sources. Saved 3 days of debugging that would have happened after deployment.&lt;/p&gt;




&lt;p&gt;100 patterns with MUnit tests: &lt;a href="https://github.com/shakarbisetty/mulesoft-cookbook" rel="noopener noreferrer"&gt;github.com/shakarbisetty/mulesoft-cookbook&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;60-second video walkthroughs: &lt;a href="https://www.youtube.com/@SanThaParv?sub_confirmation=1" rel="noopener noreferrer"&gt;youtube.com/@SanThaParv&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dataweave</category>
      <category>mulesoft</category>
      <category>tutorial</category>
      <category>debugging</category>
    </item>
    <item>
      <title>Recursive PII Masking in DataWeave: One Function for Any Depth (and the Null Trap)</title>
      <dc:creator>Shakar Bisetty</dc:creator>
      <pubDate>Sun, 05 Apr 2026 16:20:33 +0000</pubDate>
      <link>https://dev.to/thasha/recursive-pii-masking-in-dataweave-one-function-for-any-depth-and-the-null-trap-3dka</link>
      <guid>https://dev.to/thasha/recursive-pii-masking-in-dataweave-one-function-for-any-depth-and-the-null-trap-3dka</guid>
      <description>&lt;p&gt;A compliance audit found SSN values 4 levels deep in our API responses last year. One recursive function masks everything. Then null values in production crashed 400 responses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Recursive &lt;code&gt;maskPII&lt;/code&gt; function dispatches on type: Object → check fields, Array → recurse, Primitive → pass through&lt;/li&gt;
&lt;li&gt;Works at any nesting depth with one function call: &lt;code&gt;maskPII(payload)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Null values crash it — add explicit null handling before the Object case&lt;/li&gt;
&lt;li&gt;No hardcoded paths needed — the function finds PII fields at any level&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Problem: PII at Unknown Depth
&lt;/h2&gt;

&lt;p&gt;Our org chart API returns hierarchical data:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"company"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Acme Corp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ceo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Alice Chen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ssn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"123-45-6789"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alice@acme.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"reports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bob Martinez"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ssn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"234-56-7890"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"email"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"bob@acme.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"reports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Carol Nguyen"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"ssn"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"345-67-8901"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;SSN at level 1 (CEO), level 2 (VP), level 3 (Director). The compliance requirement: mask ALL of them. The depth varies per org — some go 6 levels.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Recursive Solution
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%dw 2.0
output application/json
fun maskSsn(s: String): String = "***-**-" ++ s[-4 to -1]
fun maskEmail(e: String): String = do { var parts = e splitBy "@" --- parts[0][0] ++ "****@" ++ parts[1] }
fun maskPII(data: Any): Any =
    data match {
        case obj is Object -&amp;gt; obj mapObject (value, key) -&amp;gt;
            if ((key as String) == "ssn") {(key): maskSsn(value as String)}
            else if ((key as String) == "email") {(key): maskEmail(value as String)}
            else {(key): maskPII(value)}
        case arr is Array -&amp;gt; arr map maskPII($)
        else -&amp;gt; data
    }
---
maskPII(payload)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type dispatch: Objects get field-level checking. Arrays recurse into each element. Strings, numbers, booleans pass through unchanged.&lt;/p&gt;

&lt;p&gt;One call — &lt;code&gt;maskPII(payload)&lt;/code&gt; — handles any depth.&lt;/p&gt;




&lt;p&gt;100 production-ready DataWeave patterns with tests: &lt;a href="https://github.com/shakarbisetty/mulesoft-cookbook" rel="noopener noreferrer"&gt;mulesoft-cookbook on GitHub&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Null Trap
&lt;/h2&gt;

&lt;p&gt;Production payloads had null values at level 3 — a manager with no email. The &lt;code&gt;data match&lt;/code&gt; block dispatched null to... somewhere unexpected. It tried &lt;code&gt;mapObject&lt;/code&gt; on null. Crash. 400 API responses failed.&lt;/p&gt;

&lt;p&gt;The fix: add explicit null handling.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fun maskPII(data: Any): Any =
    data match {
        case is Null -&amp;gt; null
        case obj is Object -&amp;gt; obj mapObject ...
        case arr is Array -&amp;gt; arr map maskPII($)
        else -&amp;gt; data
    }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;case is Null -&amp;gt; null&lt;/code&gt; catches null before it reaches the Object handler. Now null passes through unchanged.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Recursive Functions
&lt;/h2&gt;

&lt;p&gt;I test with these edge cases now:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Empty object &lt;code&gt;{}&lt;/code&gt; at each level&lt;/li&gt;
&lt;li&gt;Empty array &lt;code&gt;[]&lt;/code&gt; at each level&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;null&lt;/code&gt; at each level&lt;/li&gt;
&lt;li&gt;Mixed types in arrays: &lt;code&gt;["text", 42, null, {"key": "value"}]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Maximum expected depth (6 levels for our org chart)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;5 minutes of setup in the DataWeave Playground. Prevented every recursive bug since.&lt;/p&gt;




&lt;p&gt;100 patterns with MUnit tests: &lt;a href="https://github.com/shakarbisetty/mulesoft-cookbook" rel="noopener noreferrer"&gt;github.com/shakarbisetty/mulesoft-cookbook&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;60-second video walkthroughs: &lt;a href="https://www.youtube.com/@SanThaParv?sub_confirmation=1" rel="noopener noreferrer"&gt;youtube.com/@SanThaParv&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dataweave</category>
      <category>mulesoft</category>
      <category>security</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>DataWeave match/case: Replace If/Else Chains and Avoid the Missing Else Crash</title>
      <dc:creator>Shakar Bisetty</dc:creator>
      <pubDate>Sun, 05 Apr 2026 15:02:08 +0000</pubDate>
      <link>https://dev.to/thasha/dataweave-matchcase-replace-ifelse-chains-and-avoid-the-missing-else-crash-18md</link>
      <guid>https://dev.to/thasha/dataweave-matchcase-replace-ifelse-chains-and-avoid-the-missing-else-crash-18md</guid>
      <description>&lt;p&gt;I replaced a 40-line if/else chain with 4 match/case lines last month. Then I forgot the else clause and 3,000 production events backed up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;match/case&lt;/code&gt; replaces nested if/else with value matching, guard conditions, and type dispatch&lt;/li&gt;
&lt;li&gt;Missing &lt;code&gt;else&lt;/code&gt; clause causes runtime errors on unmatched values — unlike switch/default, there's no implicit fallthrough&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;event.type&lt;/code&gt; fails — &lt;code&gt;type&lt;/code&gt; is a reserved word in DataWeave, use &lt;code&gt;event."type"&lt;/code&gt; with quotes&lt;/li&gt;
&lt;li&gt;Guard conditions (&lt;code&gt;case t if t startsWith "payment."&lt;/code&gt;) handle wildcard matching&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Problem: Growing If/Else Chains
&lt;/h2&gt;

&lt;p&gt;We had an event routing flow. Orders, payments, user actions — each type needed a different processing queue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (event."type" == "order.created") { category: "ORDER", action: "PROCESS" }
else if (event."type" == "order.cancelled") { category: "ORDER", action: "REFUND" }
else if (event."type" == "payment.received") { category: "PAYMENT", action: "RECONCILE" }
else if (event."type" == "payment.refunded") { category: "PAYMENT", action: "RECONCILE" }
else if (event."type" == "user.login") { category: "CUSTOMER", action: "SYNC" }
else if (event."type" == "user.logout") { category: "CUSTOMER", action: "SYNC" }
else if (event."type" == "user.signup") { category: "CUSTOMER", action: "ONBOARD" }
else { category: "SYSTEM", action: "LOG" }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;40 lines. Growing every sprint. Hard to read. Repetitive.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Fix: match/case
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;%dw 2.0
output application/json
fun routeEvent(event: Object): Object =
    event."type" match {
        case "order.created" -&amp;gt; ({ category: "ORDER", action: "PROCESS", priority: "NORMAL" })
        case "order.cancelled" -&amp;gt; ({ category: "ORDER", action: "REFUND", priority: "HIGH" })
        case eventType if eventType startsWith "payment." -&amp;gt; ({ category: "PAYMENT", action: "RECONCILE", priority: "HIGH" })
        else -&amp;gt; ({ category: "SYSTEM", action: "LOG", priority: "LOW" })
    }
---
payload.events map (event) -&amp;gt; routeEvent(event)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4 cases. Value matching for exact types. Guard condition for the payment prefix. Else for everything unexpected.&lt;/p&gt;




&lt;p&gt;100 production-ready DataWeave patterns with tests: &lt;a href="https://github.com/shakarbisetty/mulesoft-cookbook" rel="noopener noreferrer"&gt;mulesoft-cookbook on GitHub&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Trap 1: The Missing Else Clause
&lt;/h2&gt;

&lt;p&gt;I deployed my first version without &lt;code&gt;else&lt;/code&gt;. For 3 weeks, every event was a known type. Then an "inventory.updated" event arrived — a type I hadn't handled.&lt;/p&gt;

&lt;p&gt;Runtime error. 3,000 events backed up in the queue before monitoring caught it.&lt;/p&gt;

&lt;p&gt;DataWeave's match/case does NOT have implicit fallthrough. Without &lt;code&gt;else&lt;/code&gt;, an unmatched value throws an exception. Unlike JavaScript's switch where falling through is the default, DataWeave requires explicit handling of every possible value.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule: every match block needs an else clause. No exceptions.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Trap 2: Reserved Word Field Access
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;event.type    // PARSE ERROR — "type" is a reserved word
event."type"  // works — quoted field name
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;DataWeave reserves &lt;code&gt;type&lt;/code&gt; as a keyword. When your JSON has a &lt;code&gt;type&lt;/code&gt; field (which is extremely common in event-driven architectures), you must quote it in the selector. I spent 20 minutes debugging this the first time.&lt;/p&gt;

&lt;p&gt;Other reserved words that break field selectors: &lt;code&gt;match&lt;/code&gt;, &lt;code&gt;case&lt;/code&gt;, &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;else&lt;/code&gt;, &lt;code&gt;do&lt;/code&gt;, &lt;code&gt;fun&lt;/code&gt;, &lt;code&gt;var&lt;/code&gt;, &lt;code&gt;type&lt;/code&gt;, &lt;code&gt;input&lt;/code&gt;, &lt;code&gt;output&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Guard Conditions: Pattern Matching with Logic
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;case eventType if eventType startsWith "payment."&lt;/code&gt; syntax combines pattern matching with boolean guards:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;event."type" match {
    case t if t startsWith "order." -&amp;gt; handleOrder(t)
    case t if t matches /user\.(.*)/ -&amp;gt; handleUser(t)
    case t if sizeOf(t) &amp;gt; 50 -&amp;gt; handleLongType(t)
    else -&amp;gt; handleUnknown()
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use any boolean expression after &lt;code&gt;if&lt;/code&gt;. The bound variable &lt;code&gt;t&lt;/code&gt; holds the matched value for use in both the guard and the result.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Do Now
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Every match block has an else clause before deployment&lt;/li&gt;
&lt;li&gt;Reserved word fields always use quoted selectors&lt;/li&gt;
&lt;li&gt;New event types go into the match block proactively — I review dead-letter queue weekly&lt;/li&gt;
&lt;li&gt;Code reviews flag match without else as a blocking issue&lt;/li&gt;
&lt;/ol&gt;




&lt;p&gt;100 patterns with MUnit tests: &lt;a href="https://github.com/shakarbisetty/mulesoft-cookbook" rel="noopener noreferrer"&gt;github.com/shakarbisetty/mulesoft-cookbook&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;60-second video walkthroughs: &lt;a href="https://www.youtube.com/@SanThaParv?sub_confirmation=1" rel="noopener noreferrer"&gt;youtube.com/@SanThaParv&lt;/a&gt;&lt;/p&gt;

</description>
      <category>dataweave</category>
      <category>mulesoft</category>
      <category>tutorial</category>
      <category>debugging</category>
    </item>
  </channel>
</rss>
