API Management — Mock Responses and Virtual APIs
Overview
Mock responses allow you to return sample API responses without needing a real backend. This is invaluable for parallel development - frontend teams can start integrating while the backend is still being built. You can also use mock responses for testing, documentation, and creating virtual APIs that don't call any backend at all.
What You'll Learn
- Creating mock APIs that return static responses
- Using dynamic mock responses with expressions
- Testing API contracts without backend
- Building virtual APIs for prototyping
Problem
You need to:
- Test API contracts before backend is ready
- Provide sample APIs for frontend developers
- Create mock responses for integration testing
- Build prototype APIs without backend
Solution Architecture
┌─────────────────────────────────────────────────────────────────┐
│ Client Requests │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌─────────────┐ ┌─────────────────────────┐ │
│ │ Frontend│───▶│ APIM │───▶│ Mock Response │ │
│ │ Dev │ │ Gateway │ │ Policy │ │
│ └─────────┘ └─────────────┘ └─────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Static or Dynamic Response │ │
│ │ { "products": [...], "total": 100 } │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ No backend required! │
└─────────────────────────────────────────────────────────────────┘
Implementation
Step 1: Basic Mock Response
Return a static JSON response:
<policies>
<inbound>
<mock-response status-code="200" content-type="application/json" />
</inbound>
</policies>
Step 2: Static JSON Response
<inbound>
<return-response>
<set-status code="200" reason="OK" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>{
"products": [
{ "id": 1, "name": "Product A", "price": 29.99 },
{ "id": 2, "name": "Product B", "price": 49.99 }
],
"total": 2,
"status": "success"
}</set-body>
</return-response>
</inbound>
Step 3: Dynamic Mock Response with Expressions
Generate response based on request:
<inbound>
<set-variable name="requestMethod" value="@(context.Request.Method)" />
<set-variable name="productId" value="@(context.Request.Url.Matches["/products/([^/]+)"][1])" />
<choose>
<when condition="@(context.Request.Method == "GET")">
<return-response>
<set-body>@{
var productId = context.Variables.GetValueOrDefault<string>("productId");
if (string.IsNullOrEmpty(productId)) {
return "{\"products\": ["
+ "{\"id\": 1, \"name\": \"Product A\", \"price\": 29.99},"
+ "{\"id\": 2, \"name\": \"Product B\", \"price\": 49.99}"
+ "],\"total\": 2}";
} else {
return "{\"id\":" + productId + ","
+ "\"name\":\"Product " + productId + "\","
+ "\"price\": 29.99,"
+ "\"inStock\": true}";
}
}</set-body>
</return-response>
</when>
<when condition="@(context.Request.Method == "POST")">
<return-response>
<set-status code="201" reason="Created" />
<set-body>{
"success": true,
"message": "Product created",
"id": @((int)new Random().Next(1000, 9999))
}</set-body>
</return-response>
</when>
</choose>
</inbound>
Step 4: Mock Response with Headers
<inbound>
<return-response>
<set-status code="200" reason="OK" />
<set-header name="X-Request-Id" exists-action="override">
<value>@(Guid.NewGuid().ToString())</value>
</set-header>
<set-header name="X-Rate-Limit-Remaining" exists-action="override">
<value>100</value>
</set-header>
<set-header name="X-Page-Number" exists-action="override">
<value>1</value>
</set-header>
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>{
"data": [
{ "id": 1, "name": "Item 1" },
{ "id": 2, "name": "Item 2" }
],
"pagination": {
"page": 1,
"pageSize": 10,
"totalItems": 100,
"totalPages": 10
}
}</set-body>
</return-response>
</inbound>
Step 5: Mock for Different Response Codes
Simulate various HTTP responses:
<inbound>
<set-variable name="testScenario" value="@(context.Request.Headers.GetValueOrDefault("X-Test-Scenario", "success"))" />
<choose>
<when condition="@(context.Variables.GetValueOrDefault<string>("testScenario") == "not-found")">
<return-response>
<set-status code="404" reason="Not Found" />
<set-body>{
"error": "not_found",
"message": "The requested resource was not found",
"requestId": "@(Guid.NewGuid().ToString())"
}</set-body>
</return-response>
</when>
<when condition="@(context.Variables.GetValueOrDefault<string>("testScenario") == "unauthorized")">
<return-response>
<set-status code="401" reason="Unauthorized" />
<set-body>{
"error": "unauthorized",
"message": "Invalid or missing authentication token"
}</set-body>
</return-response>
</when>
<when condition="@(context.Variables.GetValueOrDefault<string>("testScenario") == "rate-limit")">
<return-response>
<set-status code="429" reason="Too Many Requests" />
<set-header name="Retry-After" exists-action="override">
<value>60</value>
</set-header>
<set-body>{
"error": "rate_limit_exceeded",
"message": "Too many requests. Please try again later."
}</set-body>
</return-response>
</when>
<otherwise>
<!-- Default success response -->
<return-response>
<set-body>{"status": "success", "data": {}}</set-body>
</return-response>
</otherwise>
</choose>
</inbound>
Step 6: Generate Sample Data from Schema
Use Liquid template to generate mock data:
<inbound>
<set-body template="liquid">
{
"id": "{{ 100 | random }}",
"name": "Product {{ 1 | random:100 }}",
"description": "This is a sample product description",
"price": {{ 10 | random:500 }},
"category": "{{ "Electronics,Clothing,Books,Home" | split:"," | sample }}",
"inStock": {{ true | random }},
"tags": ["{{ "new,sale,bestseller,featured" | split:"," | sample }}", "{{ "limited" | sample }}"],
"createdAt": "{{ now | date: "yyyy-MM-ddTHH:mm:ssZ" }}"
}
</set-body>
</inbound>
Step 7: Create Separate Mock API
<!-- Create new Blank API with mock responses -->
<api name="mock-products-api" base-url="/mock/v1" format="json">
<!-- Operations defined in Portal -->
</api>
In the operation policy:
<inbound>
<mock-response status-code="200" content-type="application/json" />
</inbound>
Real-Time Scenarios
Scenario 1: Frontend Development Parallelization
<!-- Frontend team can test against this mock while backend is being built -->
<inbound>
<return-response>
<set-body>{
"products": [
{"id": 1, "name": "Laptop Pro", "price": 1299.99, "category": "Electronics"},
{"id": 2, "name": "Wireless Mouse", "price": 49.99, "category": "Accessories"},
{"id": 3, "name": "USB-C Hub", "price": 79.99, "category": "Accessories"}
],
"pagination": {
"page": 1,
"pageSize": 10,
"totalItems": 3,
"totalPages": 1
}
}</set-body>
</return-response>
</inbound>
Scenario 2: API Contract Testing
<!-- Test client can verify response structure matches contract -->
<inbound>
<return-response>
<set-body>{
"orders": [
{
"orderId": "ORD-12345",
"customerId": "CUST-001",
"items": [
{"productId": 1, "quantity": 2, "price": 29.99},
{"productId": 2, "quantity": 1, "price": 49.99}
],
"total": 109.97,
"status": "pending",
"createdAt": "2024-05-01T10:30:00Z"
}
]
}</set-body>
</return-response>
</inbound>
Scenario 3: Error Response Testing
<!-- Simulate various error scenarios for client error handling -->
<inbound>
<set-variable name="errorType" value="@(context.Request.Query.GetValueOrDefault("error", "none"))" />
<choose>
<when condition="@(context.Variables.GetValueOrDefault<string>("errorType") == "validation")">
<return-response>
<set-status code="400" reason="Bad Request" />
<set-body>{
"error": "validation_error",
"message": "Invalid request parameters",
"details": [
{"field": "email", "message": "Invalid email format"},
{"field": "quantity", "message": "Must be greater than 0"}
]
}</set-body>
</return-response>
</when>
</choose>
</inbound>
Scenario 4: Delay Simulation
<!-- Simulate slow responses for timeout testing -->
<inbound>
<wait seconds="2" />
<return-response>
<set-body>{"message": "This response was delayed by 2 seconds"}</set-body>
</return-response>
</inbound>
Testing Mock APIs
# Test GET all products
curl -X GET "https://my-apim.azure-api.net/mock/v1/products"
# Test GET single product
curl -X GET "https://my-apim.azure-api.net/mock/v1/products/123"
# Test POST (create)
curl -X POST "https://my-apim.azure-api.net/mock/v1/products" \
-H "Content-Type: application/json" \
-d '{"name": "New Product", "price": 99.99}'
# Test error scenarios
curl -X GET "https://my-apim.azure-api.net/mock/v1/products?error=validation"
curl -X GET "https://my-apim.azure-api.net/mock/v1/products?error=not-found"
Best Practices
| Practice | Why |
|---|---|
| Use for parallel dev | Frontend can start immediately |
| Document mock behavior | Teams know what's mocked |
| Version mock APIs | Don't break existing consumers |
| Include realistic data | Better testing and demos |
| Test error scenarios | Verify client error handling |
Summary
- Use mock responses to return sample data without backend
- Dynamic responses can use request data and expressions
- Test various HTTP status codes and error scenarios
- Enable parallel development between frontend and backend teams