Brahim Bousnguar

Learn · MuleSoft · Lesson 1 of 8

Mule apps and flows

By · · 7 min read · 5 live labs

A Mule app is a set of flows, and a flow is the path one Mule event takes from a source to the end. Get those two ideas right and the rest of Mule 4 gets a lot easier: error handlers, routers, even why your payload vanished after an HTTP call.

This lesson follows one small orders app through five live diagrams. Each one plays by itself while it's on screen. The buttons above it change the story: take the stock API down, swap the error handler, pick a router. Pause it any time and step through with the arrows.

One app, many flows

A Mule app ships as one JAR. The flows live in XML files under src/main/mule, next to a pom.xml and a mule-artifact.json that lists those files. You deploy that JAR to CloudHub 2.0, to Runtime Fabric or to a Mule runtime on your own servers.

Flows don't own their connections. A global element, like an HTTP Listener config or a Database config, is declared once, and every flow in the app points at it. Anything that changes between environments goes in a property placeholder such as ${http.port}, not in the XML.

Lab 1 · One app, two flows

App deployed

The orders app: two flows, post-order started by an HTTP request and nightly-report started by a scheduler, both using shared global elements for the HTTP listener, the stock API and the database.

What starts a flow

Global elements · shared by every flow

  1. HTTP Listener configport ${http.port}
  2. HTTP Request configstock-api · ${stock.host}
  3. Database configorders-db

flow post-order

  1. HTTP ListenerPOST /orders
  2. HTTP RequestGET /stock/{sku}
  3. Loggerorder received

flow nightly-report

  1. Schedulerevery day at 02:00
  2. Databaseselect yesterday's orders
  3. Loggerreport line

Each flow starts from its own source. post-order waits for a request, nightly-report waits for 02:00. They share configuration, never an event.

The Mule event is what moves

Everything a flow does, it does to a Mule event. The event has two parts: the message, which is a payload plus attributes, and the variables.

  • payload is the data. For an HTTP request, it's the body.
  • attributes say where the data came from: method, path, headers, query parameters.
  • vars are yours. Set Variable writes them, and they stay with the event for the rest of the flow.

The catch: almost every processor returns a new message. Call another API with HTTP Request and its answer becomes the payload, its status code lands in the attributes, and the order the client sent is gone. Unless you saved what you need in a variable first, or told the call to put its result somewhere else with target.

Lab 2 · The Mule event, step by step

Request in

One order going through post-order: the HTTP Listener turns the request into a Mule event, Set Variable saves the SKU, and the HTTP Request either replaces the payload with the stock answer or, with target="stock", stores it in vars.stock and leaves the message unchanged.

The stock call's result goes to

flow post-order

  1. HTTP ListenerPOST /orders
  2. Set Variablesku = #[payload.sku]
  3. HTTP RequestGET /stock/#[vars.sku]
  4. Logger#[payload]

Mule event

payload
{ "sku": "SOFA-D5", "qty": 1 }
attributes
method: "POST"
requestPath: "/orders"
headers: { "content-type": "application/json", … }
queryParams: {}
vars
(none yet)

Switch to target="stock" and watch the payload row. It doesn't flash at the HTTP Request step this time: the answer lands in vars.stock and the order is still the payload when the listener replies. The Logger, in both runs, changes nothing.

Errors take another path

When a processor fails, Mule raises an error with a type like HTTP:CONNECTIVITY, skips the rest of the flow and goes to the flow's error handler. The handler runs the first On-Error whose type matches. There are two kinds, and they answer the client very differently.

  • On Error Propagate runs its processors, then throws the error again. The HTTP Listener answers 500 with the error's description as the body. The payload you set in the handler isn't sent unless you configure the listener's error response.
  • On Error Continue runs its processors, then ends the flow as a success. The listener answers 200 with whatever the payload is.

And if the flow has no handler at all, Mule's default one logs the error and propagates it.

Lab 3 · When the stock API is down

Request in

post-order calls the stock API. When the API is up, the flow ends with 200. When it is down, the HTTP Request raises HTTP:CONNECTIVITY: with On Error Propagate the client gets a 500 with the error description, with On Error Continue it gets a 200 with the handler's fallback payload.

Stock API
Error handler

flow post-order

  1. HTTP ListenerPOST /orders
  2. Set Variablesku
  3. HTTP RequestGET /stock/#[vars.sku]
  4. Loggerorder received

error handler

  1. On Error Propagatetype: ANY
  2. Set Payload{ message: "Stock check failed" }

Mule event

payload
{ "sku": "SOFA-D5", "qty": 1 }
vars
(none yet)

Run "down" with On Error Continue and look at the result: the stock API is down and the client gets a 200. Sometimes that's exactly what you want, a fallback answer. If the client needs to know something failed, set the status code yourself.

Flow Reference: sub-flow or flow

Flow Reference calls another flow of the same app with the same event. Variables travel with it, so the called flow can read vars.sku, and the payload it leaves behind is the caller's payload when it returns. What you call comes in two shapes.

  • A sub-flow has no source and no error handler. Mule puts its processors in place of the Flow Reference when it builds the app, so it really is part of the caller.
  • A flow without a source can have its own error handler. An error inside it goes to that handler first, and only reaches the caller if the handler propagates it.

Lab 4 · Flow Reference: sub-flow or flow

Request in

post-order calls check-stock with a Flow Reference. If check-stock is a sub-flow and the stock call fails, post-order's own error handler runs and the client gets a 500. If check-stock is a flow with its own On Error Continue handler, that handler runs instead, the Flow Reference returns normally and post-order answers 200.

check-stock is a
Stock API

flow post-order

  1. HTTP ListenerPOST /orders
  2. Set Variablesku
  3. Flow Referencecheck-stock
  4. Loggerorder received

sub-flow check-stock · no source, no error handler

  1. HTTP RequestGET /stock/#[vars.sku]

post-order error handler

  1. On Error Propagatetype: ANY

Mule event

payload
{ "sku": "SOFA-D5", "qty": 1 }
vars
(none yet)

So when check-stock has its own On Error Continue, post-order never sees the failure. Its Flow Reference just returns and the Logger runs. Great for a fallback, and easy to miss when you're reading logs and wondering why the caller's handler stayed quiet.

Routers and scopes change the path

A flow doesn't have to be a straight line.

  • Choice tests its routes in order and runs the first one whose condition is true. If none is, the otherwise route runs; it's optional.
  • Scatter-Gather sends the event to every route at once, each on its own thread, and waits for all of them. The result is one payload with a key per route: 0, 1 and so on.
  • For Each runs its processors once per item of a collection. It doesn't change the payload: after the loop you get the original back. Variables you set inside the loop stay set.

Lab 5 · Routers and scopes

Order in

Three flows. A Choice router sends an order to the Nantes route for FR, the New Jersey route for US, and to a Raise Error in the otherwise route for any other country. A Scatter-Gather calls the stock API and the price API at the same time and returns one object keyed 0 and 1. A For Each reserves stock once per order item and leaves the payload as the original order.

Pick one
Order country

flow route-order

  1. HTTP ListenerPOST /orders
  2. Choiceby payload.country

    country == "FR"

    1. HTTP RequestNantes warehouse

    country == "US"

    1. HTTP RequestNew Jersey warehouse

    otherwise

    1. Raise ErrorAPP:UNSUPPORTED_COUNTRY
  3. Loggerrouted

Mule event

payload
{ "orderId": "A-1042", "country": "FR", "sku": "SOFA-D5" }
vars
(none yet)

Scatter-Gather is the one to reach for when two calls don't depend on each other: the quote waits for the slower call, not for both in a row. If a route fails, Mule still waits for the others, then raises MULE:COMPOSITE_ROUTING with what failed and what worked inside.

Check yourself

Three questions on what you just ran. Pick an answer to see why.

  1. The HTTP Request in post-order has target="stock". After it runs, what is the payload?

    • The stock API's answer
    • The order the client posted
    • Nothing: the payload is cleared
    Answer

    The order the client posted. With target, the call's result goes into vars.stock and the message passes through unchanged.

  2. The stock API is down, and post-order's error handler is On Error Continue with a Set Payload. What does the client get?

    • A 500 with the error's description
    • No response at all
    • A 200 with the handler's payload
    Answer

    A 200 with the handler's payload. On Error Continue ends the flow as a success, so the listener answers as if nothing failed.

  3. post-order calls check-stock with a Flow Reference. check-stock is a sub-flow, and the stock call inside it fails. Which error handler runs?

    • post-order's error handler
    • check-stock's own error handler
    • None: sub-flows ignore errors
    Answer

    post-order's. A sub-flow has no error handler: its processors run in place of the Flow Reference, so the error goes to the caller's handler, or to a Try scope around the Flow Reference.

Where the facts come from

Every claim in this lesson was checked against the Mule 4 documentation:

That's the base the rest of the track builds on. Next up is watermarks: how a polling flow remembers where it stopped. If a lab does something Mule wouldn't, tell me. The contact links are right below.

Plain text: mule-app-flows.md · All tracks

Contact

Say hello.

b.bousnguar@gmail.com

SAP Commerce Cloud, MuleSoft and Salesforce integration · Nantes · EN / FR