DEV Community

Cover image for How to Return Conditional Mock Data in Apidog (Custom Rules and Mock Scripts)
Hassann
Hassann

Posted on • Originally published at apidog.com

How to Return Conditional Mock Data in Apidog (Custom Rules and Mock Scripts)

Smart mock creates a fake API in seconds. It reads your endpoint schema and returns plausible data: realistic emails, sensible timestamps, and usable names. For most frontend work, that is enough to unblock development.

Try Apidog today

But some cases require request-aware behavior. You may want /login to return 200 for a known user and 401 for everyone else. Or /orders/{id} to return a shipped order for one ID and a cancelled order for another. You may also need to force a 500 so error handling is tested before production.

Smart mock returns one generated response shape per endpoint, so it cannot branch on the incoming request. This guide shows how to fill that gap with:

  • Mock expectations for rule-based conditional responses.
  • Mock scripts for logic that fixed rules cannot express.

For background, see the API mocking overview. This tutorial uses Apidog and follows the contract-first workflow described by the OpenAPI Initiative.

What conditional mocking means

A conditional mock is a rule:

When the request looks like this, return that response.

Apidog provides two layers for mocking.

  1. Field-level customization inside an endpoint schema

    Use fixed values or dynamic Faker.js expressions. This controls individual field values, but the endpoint still has one response shape.

  2. Full-response mock expectations

    An expectation is a named rule with optional conditions, a response body, status code, headers, and delay.

An expectation with no conditions always returns its configured response. An expectation with conditions responds only when the request matches. By combining expectations, you can return:

  • A different body for each path parameter.
  • An error when a required header is absent.
  • A specific status code for a known request body.
  • A fallback response for every other request.

Field-level dynamic values first

Before adding request-based branches, configure dynamic values for a single response shape.

In an endpoint schema, string fields can use Faker.js expressions in the format {{$category.method}}. Apidog resolves these expressions on every mock call using the types declared in your JSON Schema.

{
  "id": "{{$number.int(min=1000,max=9999)}}",
  "customer": "{{$person.fullName}}",
  "email": "{{$internet.email}}",
  "product": "{{$commerce.productName}}",
  "shippingAddress": "{{$location.streetAddress}}, {{$location.city}}",
  "orderedAt": "{{$date.between(from='2024-01-01',to='2024-12-31',format='yyyy-MM-dd')}}"
}
Enter fullscreen mode Exit fullscreen mode

Parameterized methods let you constrain generated values:

  • {{$number.int(min=1000,max=9999)}} generates IDs in a known range.
  • {{$date.between(...)}} constrains dates and output format.
  • You can combine static text and multiple expressions in one field.

Apidog also supports configurable mock locales for region-specific names, addresses, and phone numbers. See the Faker.js reference in Apidog for available methods.

Smart mock configuration

This is Smart mock territory: values are dynamic, but the response is not conditional. To branch based on the request, use expectations.

Walkthrough: return 200 or 401 from POST /login

Assume POST /login accepts this JSON body:

{
  "username": "alice@example.com",
  "password": "whatever"
}
Enter fullscreen mode Exit fullscreen mode

The desired behavior is:

  • alice@example.com receives 200 and a token.
  • Every other user receives 401.

1. Open the expectation list

The UI location depends on your working mode:

  • In DEBUG (Request-first) mode, open the endpoint and select the Mock tab.

Mock tab in DEBUG mode

  • In DESIGN (Design-first) mode, open the endpoint and select the Advanced mock tab.

Advanced mock tab in DESIGN mode

Both views open the same expectation list. If needed, download Apidog and create or import a /login endpoint first.

2. Add the success expectation

Click New expectation and configure the following:

  • Expectation name: login-success
  • Condition type: body parameter
  • Name: username
  • Condition: equals
  • Value: alice@example.com

Login expectation condition

For JSON request bodies, body conditions use the JSON path entered in the name field. For nested properties, use dot notation such as user.email.

Set Response data to:

{
  "token": "mock-jwt-{{$string.uuid}}",
  "user": {
    "id": 4821,
    "username": "alice@example.com",
    "role": "member"
  }
}
Enter fullscreen mode Exit fullscreen mode

Save the expectation. The default HTTP status is 200, so no additional status configuration is required.

3. Add the catch-all failure expectation

Create another expectation:

  • Expectation name: login-failure
  • Conditions: none

Leaving conditions blank makes this rule a catch-all. Set Response data to:

{
  "error": "invalid_credentials",
  "message": "Username or password is incorrect."
}
Enter fullscreen mode Exit fullscreen mode

Open the expectation’s More tab and set:

  • HTTP Status Code: 401

The More tab also lets you configure:

  • Response Delay in milliseconds, defaulting to 0.
  • Custom response headers.

For example, set a 400 ms delay to verify that your loading UI appears during login.

4. Put expectations in the correct order

Expectations run from top to bottom, and the first matching rule wins.

Order the rules like this:

  1. login-success
  2. login-failure

The success rule must be first. If the blank-condition failure rule comes first, it matches every request and the success rule never runs.

Test both responses using the endpoint’s mock URL:

# Known user -> 200 with a token
curl -X POST https://<your-mock-host>/login \
  -H "Content-Type: application/json" \
  -d '{"username":"alice@example.com","password":"whatever"}'

# Any other user -> 401
curl -X POST https://<your-mock-host>/login \
  -H "Content-Type: application/json" \
  -d '{"username":"stranger@example.com","password":"whatever"}'
Enter fullscreen mode Exit fullscreen mode

Walkthrough: return different /orders/{id} bodies

Path parameters are another common conditional-mocking use case.

For example, configure /orders/{id} so that:

  • /orders/5001 returns a shipped order.
  • /orders/5002 returns a cancelled order.
  • Any other ID returns a generic pending order.

Create one expectation for each state.

Shipped order

Create an expectation named order-shipped:

  • Condition type: path parameter
  • Parameter: id
  • Condition: equals
  • Value: 5001

Use this response body:

{
  "id": 5001,
  "status": "shipped",
  "total": 129.9,
  "trackingNumber": "1Z{{$string.alphanumeric(length=16)}}",
  "shippedAt": "{{$date.recent(days=3,format='yyyy-MM-dd')}}"
}
Enter fullscreen mode Exit fullscreen mode

Cancelled order

Create an expectation named order-cancelled:

  • Condition type: path parameter
  • Parameter: id
  • Condition: equals
  • Value: 5002

Use this response body:

{
  "id": 5002,
  "status": "cancelled",
  "total": 0,
  "cancelledAt": "{{$date.recent(days=1,format='yyyy-MM-dd')}}",
  "refundIssued": true
}
Enter fullscreen mode Exit fullscreen mode

Add a fallback

Add one final expectation with no conditions. Return a generic pending order so unknown IDs still get a valid response.

Place the rules in this order:

  1. order-shipped
  2. order-cancelled
  3. Generic pending-order fallback

You can combine condition types. For example, add both a path parameter condition and a header condition. Apidog combines multiple conditions with AND logic, so every configured condition must match.

Conditions can target:

  • Request body parameters
  • Path parameters
  • Query parameters
  • Headers
  • Cookies
  • IP addresses

Force error states on demand

You do not need a failing backend to test frontend error handling.

To force a 500 response, add an expectation that matches a client-controlled header:

  • Header: X-Mock-Scenario
  • Condition: equals
  • Value: server-error
  • HTTP Status Code: 500

Set the response body to:

{
  "error": "internal_error",
  "requestId": "{{$string.uuid}}",
  "message": "Something went wrong on our end. Please retry."
}
Enter fullscreen mode Exit fullscreen mode

Now the endpoint can return its usual 200 response by default and return 500 only when the client sends:

X-Mock-Scenario: server-error
Enter fullscreen mode Exit fullscreen mode

Use the same pattern for other states:

  • 404 for missing resources
  • 429 with a Retry-After response header
  • 503 for temporary unavailability

If you validate these cases in tests, pair this approach with API assertions.

In shared projects, each expectation can be independently enabled or disabled for local and cloud mock environments. For example, keep a forced 500 rule enabled locally but disabled in the cloud mock used by teammates.

When expectations are not enough: mock scripts

Expectations are declarative: they match requests and return predefined responses. They do not compute values.

Use a mock script when the response must be derived from the incoming request, such as:

  • Summing order line items.
  • Calculating tax.
  • Changing response shape based on several inputs.
  • Echoing request headers into the response.

Mock scripts are JavaScript and run in the Mock Script section at the bottom of the Mock tab.

The runtime exposes two globals:

  • $$.mockRequest for reading the incoming request.
    • getParam(key)
    • headers
    • cookies
    • body
    • formdata
    • urlencoded
  • $$.mockResponse for changing the outgoing response.
    • setBody()
    • setCode()
    • setDelay()
    • json()
    • headers
    • code

For example, this script calculates an order total from posted line items and returns the caller’s currency header:

const body = $$.mockRequest.body;
const items = body.items || [];

const subtotal = items.reduce((sum, item) => {
  return sum + item.price * item.quantity;
}, 0);

const currency = $$.mockRequest.headers["x-currency"] || "USD";

$$.mockResponse.setCode(201);
$$.mockResponse.setBody({
  orderId: Math.floor(Math.random() * 90000) + 10000,
  currency: currency,
  subtotal: subtotal,
  tax: Number((subtotal * 0.08).toFixed(2)),
  total: Number((subtotal * 1.08).toFixed(2))
});
Enter fullscreen mode Exit fullscreen mode

The execution flow is:

  1. Smart mock generates an initial response.
  2. The script reads $$.mockRequest and the current $$.mockResponse.
  3. The script applies custom logic.
  4. The script calls setBody(), setCode(), setDelay(), or modifies headers.
  5. Apidog returns the final response.

Use the MDN JavaScript reference when you need more complex array, date, or object logic.

Important: scripts do not run with expectations

Mock scripts work only with Smart mock.

They do not apply to:

  • Mock expectations
  • Response examples

If an expectation matches a request, the mock script does not run.

Choose one approach per endpoint:

  • Use expectations for fixed, rule-based branching.
  • Use mock scripts for computed output built on Smart mock.

How mock priority works

For every mock request, Apidog resolves the response in this order:

  1. Evaluate expectations from top to bottom

    The first expectation whose conditions all match wins. Its response is returned immediately.

  2. Fall back to Mock method priority

    If no expectation matches, Apidog uses the priority configured in:

   Project Settings → Feature Settings → Mock Settings
Enter fullscreen mode Exit fullscreen mode

This is where Smart mock, including any attached mock script, generates the response.

Use this mental model:

Specific rules first, generated data second.

Put the most specific expectations first, place a blank-condition fallback at the bottom when you need a guaranteed response, and let Smart mock handle the remaining cases.

For more examples, see the API mocking use cases guide.

Gotchas to check before shipping

Keep these constraints in mind:

  • Parameter conditions do not support {{variables}}. Apidog project and environment variables are unavailable in mock expectations, so use literal values.
  • Body-parameter conditions support JSON only. Use JSON paths in the condition name field.
  • The request body format in the condition must match the API specification. For a form-data endpoint, configure a form-data condition rather than a JSON condition.
  • Mock scripts have no logging function.
  • The pm object is unavailable in mock scripts because they run in a different environment from test scripts.
  • Apidog variables cannot be used inside mock scripts, so keep script logic self-contained.

The docs do not describe plan gating for these features. The local-versus-cloud difference is functional: expectations can be toggled independently in each environment.

Automate the workflow with the Apidog CLI

Mocking itself is a GUI and cloud capability. The mock engine serves endpoints through local and cloud mock URLs; there is no CLI command to start a running mock server.

The Apidog CLI helps manage the API resources that mocks are generated from.

Because mock responses come from endpoint schemas, mock accuracy depends on spec accuracy. The CLI and AI coding agents that use it—such as Cursor, Claude Code, Trae, and Codex—can create and update endpoints and schemas in a project.

A practical workflow looks like this:

  1. Update the API contract in code.
  2. Sync the contract to the project.
  3. Let the mock output reflect the updated schema.
  4. Run test scenarios in CI against the real backend.

Run a scenario headlessly with:

apidog run -t <scenario_id> -e <env_id> -r cli
Enter fullscreen mode Exit fullscreen mode

This executes test scenarios and reports results, letting the mock contract and backend verification use the same source of truth.

See the Apidog CLI installation guide for setup and the Apidog CLI in GitHub Actions guide for CI integration.

FAQ

Why is my expectation ignored even though the condition looks correct?

Check ordering and request format. Expectations run top to bottom, so a broad blank-condition expectation above a specific one will match first. Also verify that the condition uses the request format defined in the API spec: JSON paths for JSON bodies and form-data placement for form endpoints. Review the API mocking overview if needed.

Can I use a mock script and a mock expectation for the same response?

No. Mock scripts only run with Smart mock. They do not run for mock expectations or response examples. Use expectations for rule-based branches and scripts for computed responses.

How do I return 401 or 500 without breaking the default 200 response?

Create a dedicated expectation with a condition controlled by the client, such as a header. In the expectation’s More tab, set the HTTP status code. The default response remains 200, and the error response is returned only when the condition matches.

Can conditions use environment variables?

No. Apidog {{variable}} values are unavailable in mock expectations, and parameter conditions do not support {{variables}}. Use literal condition values.

What happens when no expectation matches?

Apidog falls back to the Mock method priority configured under Project Settings → Feature Settings → Mock Settings. Smart mock then generates a response from the schema. Add a blank-condition expectation when you need a specific fallback response instead.

Wrapping up

Smart mock handles generated schema-based data. Mock expectations handle request-aware behavior:

  • 200 for a known user and 401 for everyone else.
  • Different order bodies for different IDs.
  • A forced 500 for error-state testing.

Use mock scripts only when you need computed output that declarative rules cannot express. Remember the priority order: matching expectations run first, and Smart mock is the fallback.

Download Apidog to build your first conditional mock.

Top comments (0)