← Back to ArticlesAPI Management

API Management — Mock Responses and Virtual APIs

Creating mock APIs, returning sample responses without backend, and testing API contracts with Azure APIM.

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


Problem

You need to:


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

PracticeWhy
Use for parallel devFrontend can start immediately
Document mock behaviorTeams know what's mocked
Version mock APIsDon't break existing consumers
Include realistic dataBetter testing and demos
Test error scenariosVerify client error handling

Summary