Write the Contract Before You Write the Flow
Design-first APIs with RAML: a spec you can mock and share before any code exists, then scaffold straight into Mule flows with APIkit.
The last post ended on a promise: the layers only pay off if teams can agree on a contract and then build against it independently. This post is about writing that contract — on purpose, before the flow.
The instinct, coming from application code, is to build the API and let its shape emerge from the implementation. Ship the flow, see what JSON falls out, document it after the fact. That’s code-first: your consumers can’t start until you’re done, and the “spec” is whatever the code happened to produce. Design-first flips it: you write a machine-readable description of the API — resources, methods, payloads, examples — and that becomes the source of truth. The mobile team codes against it while you’re still deciding which database to use.
RAML in one screen
MuleSoft’s native contract language is RAML — the RESTful API Modeling Language. It’s YAML-flavored, deliberately readable, and built to describe REST APIs the way you’d whiteboard them. Here’s a small but complete spec for the Orders System API from the last post:
#%RAML 1.0
title: Orders System API
version: v1
baseUri: https://orders.api.example.com/{version}
mediaType: application/json
types:
Order:
type: object
properties:
id: string
customerId: string
status:
enum: [PLACED, SHIPPED, DELIVERED, CANCELLED]
total: number
placedAt: datetime
/orders:
/{orderId}:
get:
description: Fetch a single order by id
responses:
200:
body:
application/json:
type: Order
example:
id: "ord-8842"
customerId: "cust-119"
status: SHIPPED
total: 129.90
placedAt: 2026-05-30T14:12:00Z
404:
body:
application/json:
example:
message: "No order with that id"
Read it top to bottom. The header names the API and its baseUri. types defines a reusable data type — Order — that you declare once and reference everywhere it appears, so the schema lives in exactly one place. Then the resource tree: /orders/{orderId}, a GET, and the responses it can return, each with a real example. Those examples aren’t decoration; the mocking service is about to turn them into a live endpoint.
RAML rewards reuse the same way the architecture does. Data types, traits (shared behaviors like pagination), and resourceTypes (shared resource shapes) let a large spec stay DRY instead of copy-pasting the same 404 block into forty places.
Designing, mocking, sharing
You write RAML in API Designer, the browser-based editor inside Anypoint Platform. It’s a split view — RAML on the left, a rendered, clickable API console on the right — so a typo in your resource tree shows up immediately as a broken console.
The feature that makes design-first click is the mocking service. Flip a switch and API Designer stands up a live endpoint backed by nothing but your example values. Call GET /orders/ord-8842 against the mock URL and it returns that JSON — no flow, no database, no deploy. Your consumers integrate against a working URL on day one, and you find the awkward parts of the contract (a missing field, a wrong status code) while they’re still cheap to change, in the spec, not in production.
Once the contract stabilizes, you publish it to Anypoint Exchange — MuleSoft’s asset catalog. Exchange turns the spec into a discoverable, versioned asset with generated documentation and that same interactive console. Now the RAML isn’t a file on someone’s laptop; it’s the canonical Orders System API v1 that any team in the org can find, read, and depend on. This is the governance seam the API-led model kept pointing at: one place where “what does this API promise” lives.
From spec to flows with APIkit
Here’s where MuleSoft closes the loop and design-first stops being a documentation exercise. APIkit reads your RAML and generates a running Mule scaffold from it.
In Anypoint Studio you point Import from Exchange (or a local RAML file) at the spec, and APIkit builds:
- a main flow with an APIkit Router that parses each incoming request, validates it against the RAML, and dispatches to the right implementation flow;
- one implementation flow per resource-and-method —
get:\orders\(orderId):orders-configfor ourGET, pre-named and empty, waiting for you to drop in the logic; - a wired-up error handler that returns the RAML-defined
404/400/405responses automatically.
The generated router looks like this:
<flow name="orders-main">
<http:listener config-ref="orders-http" path="/api/*"/>
<apikit:router config-ref="orders-config"/>
</flow>
<flow name="get:\orders\(orderId):orders-config">
<!-- your implementation goes here -->
</flow>
The router does the routing and the validation. Send a request the RAML doesn’t allow — a bad method, a payload that violates the Order type — and APIkit rejects it before your flow ever runs, using the contract as the gatekeeper. You fill in only the business logic; the request-shape enforcement is inherited from the spec for free.
And because the flow is generated from the contract rather than the contract being scraped from the flow, the two can’t quietly drift. Change the RAML, regenerate, and APIkit adds flows for the new resources while leaving your existing implementations alone.
What about OpenAPI?
RAML is MuleSoft’s heritage format, but you’re not locked into it. Anypoint supports OAS / OpenAPI for design, mocking, publishing to Exchange, and APIkit scaffolding, so a shop already standardized on OpenAPI can bring its specs along. The workflow is identical — spec first, mock, publish, scaffold. RAML tends to be terser for the reuse features (types and traits read especially cleanly), which is why a lot of MuleSoft estates still reach for it, but the discipline is what matters, not the dialect.
Final thoughts
Design-first feels slower for the first hour — you’re writing YAML instead of dragging components onto a canvas. It pays that back the moment a second person is involved. The consumer starts against a mock instead of waiting for your code. The spec in Exchange settles the “what does this return” arguments before they happen. And APIkit means the contract isn’t aspirational documentation that rots — it’s the thing that generates and guards your flows.
A reusable API estate lives or dies on its contracts. Write them first, make them real with a mock, publish them where people can find them, and let APIkit turn them into scaffolding. Then all that’s left is the interesting part: the transformation logic inside each flow — which is exactly where we’re headed next.
Next: the DataWeave deep dive — higher-order functions, groupBy, reduce, and the transforms that separate copy-paste DataWeave from the real thing.
Comments