MuleSoft Building REST APIs
Building a REST API in MuleSoft means creating an application that listens for HTTP requests, processes the data, and returns structured responses. MuleSoft makes this straightforward with the HTTP Connector and the APIkit Router. This topic walks through building a complete CRUD REST API for managing products.
What Is a REST API
REST (Representational State Transfer) is the most common style for building APIs. REST APIs use standard HTTP methods to perform actions on resources:
- GET: Read or retrieve data
- POST: Create new data
- PUT: Replace existing data completely
- PATCH: Update part of existing data
- DELETE: Remove data
REST API Resource Map for Products
HTTP Method | Endpoint | Action
------------|----------------------|-----------------------------
GET | /products | Get all products
GET | /products/{id} | Get one product by ID
POST | /products | Create a new product
PUT | /products/{id} | Replace a product
PATCH | /products/{id} | Partially update a product
DELETE | /products/{id} | Delete a product
HTTP Status Codes for REST APIs
Every REST response includes an HTTP status code that tells the caller whether the request succeeded or failed.
Status Code Reference
Code | Meaning -----|------------------------------- 200 | OK — request succeeded 201 | Created — new record created 204 | No Content — success, nothing to return (used for DELETE) 400 | Bad Request — invalid input from client 401 | Unauthorized — authentication required 403 | Forbidden — authenticated but not permitted 404 | Not Found — resource does not exist 409 | Conflict — duplicate or state conflict 422 | Unprocessable Entity — validation failed 500 | Internal Server Error — server-side problem 503 | Service Unavailable — dependency is down
Building the Product API in MuleSoft
Start by creating a new Mule project in Anypoint Studio. The project will have one file with multiple flows — one flow per endpoint.
Flow Structure for Product API
products-api.xml:
|
+-- getAllProductsFlow
| [HTTP Listener: GET /products]
| [Database: SELECT * FROM products]
| [Transform: rows to JSON array]
|
+-- getProductByIdFlow
| [HTTP Listener: GET /products/{id}]
| [Database: SELECT * FROM products WHERE id = #[attributes.uriParams.id]]
| [Choice: found? 200 : 404]
|
+-- createProductFlow
| [HTTP Listener: POST /products]
| [Validate: check required fields]
| [Database: INSERT INTO products...]
| [Set HTTP Status: 201]
|
+-- updateProductFlow
| [HTTP Listener: PUT /products/{id}]
| [Database: UPDATE products SET... WHERE id=...]
| [Choice: updated? 200 : 404]
|
+-- deleteProductFlow
[HTTP Listener: DELETE /products/{id}]
[Database: DELETE FROM products WHERE id=...]
[Set HTTP Status: 204]
[Set Payload: empty]
Configuring the HTTP Listener for Each Method
Each flow needs its own HTTP Listener. All listeners share the same connector configuration (host and port) but differ in path and allowed methods.
HTTP Listener Properties for Each Endpoint
getAllProductsFlow Listener:
Config: HTTP_Listener_Config (0.0.0.0:8081)
Path: /products
Method: GET
createProductFlow Listener:
Config: HTTP_Listener_Config (same config, shared)
Path: /products
Method: POST
getProductByIdFlow Listener:
Config: HTTP_Listener_Config (same config)
Path: /products/{id} ← URI parameter
Method: GET
updateProductFlow Listener:
Config: HTTP_Listener_Config (same config)
Path: /products/{id}
Method: PUT
deleteProductFlow Listener:
Config: HTTP_Listener_Config (same config)
Path: /products/{id}
Method: DELETE
Reading URI Parameters
When a path contains {id}, MuleSoft captures whatever the caller puts there. Access it using attributes.uriParams.id.
Request: GET /products/PROD-007 In the flow: attributes.uriParams.id = "PROD-007" Database query: SELECT * FROM products WHERE id = '#[attributes.uriParams.id]' → SELECT * FROM products WHERE id = 'PROD-007'
Reading Query Parameters
Query parameters appear after the question mark in a URL. Access them with attributes.queryParams.paramName.
Request: GET /products?category=electronics&maxPrice=500 In the flow: attributes.queryParams.category = "electronics" attributes.queryParams.maxPrice = "500" DataWeave to build the WHERE clause: "SELECT * FROM products WHERE category = '" ++ attributes.queryParams.category ++ "' AND price <= " ++ attributes.queryParams.maxPrice
Reading the Request Body
For POST and PUT requests, the request body becomes the payload. Parse it with a Transform Message if needed.
POST /products
Content-Type: application/json
Body:
{
"name": "Wireless Mouse",
"category": "electronics",
"price": 29.99,
"stock": 150
}
In the flow:
payload.name = "Wireless Mouse"
payload.category = "electronics"
payload.price = 29.99
payload.stock = 150
Database Insert:
INSERT INTO products (name, category, price, stock)
VALUES (#[payload.name], #[payload.category], #[payload.price], #[payload.stock])
Setting the HTTP Response Status Code
Use the HTTP Response settings or the Set Variable component to set the status code. In the HTTP Listener's response section, reference a variable for the status code.
For a successful POST (create):
[Set Variable: name="httpStatus", value=201]
[HTTP Listener Response: status=#[vars.httpStatus]]
For a 404 Not Found:
[Set Variable: name="httpStatus", value=404]
[Set Payload: {"error": "Product not found"}]
Complete GET by ID Flow Example
Flow: getProductByIdFlow
[HTTP Listener: GET /products/{id}]
|
v
[Database: SELECT * FROM products WHERE id = '#[attributes.uriParams.id]']
|
v
[Choice Router]
Condition: sizeOf(payload) > 0
|
+-- TRUE:
| [Transform: rows[0] to JSON object]
| [Set Variable: httpStatus = 200]
|
+-- FALSE (no record found):
[Set Payload: {"error": "Product not found"}]
[Set Variable: httpStatus = 404]
|
v
[HTTP Listener Response: status=#[vars.httpStatus], body=#[payload]]
Input Validation
Always validate request data before processing. Use a Choice Router to check required fields and return a 400 error with a descriptive message if validation fails.
[Choice Router: validate POST body]
Condition: payload.name == null or payload.price == null
|
+-- TRUE (invalid):
| [Set Payload: {"error": "name and price are required"}]
| [Set Variable: httpStatus = 400]
| (skip database, go to response)
|
+-- FALSE (valid):
[Database Insert]
[Set Variable: httpStatus = 201]
Testing the API with Postman
Run the Mule application locally and open Postman. Create a collection named "Product API" and add requests for each endpoint. Test each HTTP method with valid and invalid data to verify the correct status codes and response bodies are returned. Always test error scenarios like missing fields, invalid IDs, and duplicate records.
