Response Configuration

Overview

Once API Simulator matches a request and determines which simlet it is for, it constructs the response to return based on the simlet response configuration.

Notice that by default API Simulator streams the response back as it is being built. This makes it possible for API Simulator to return very large responses while running with only few MB of memory.

There are two types of responses with respect to the data they return:

  • Static stubs/mocks, and

  • Dynamic responses.

Static Stubs/Mocks

Static stubs/mocks are practically canned responses - responses that don’t change between requests for the same simlet.

Dynamic Responses

Dynamic responses, as their name suggests, are built dynamically per request and can change from one request for a simlet to the next.

Response Configuration

API Simulator looks in the response section of a simlet configuration for what response to return. The configuration includes fields for data (static or dynamic) and behavior (latency, connection disruption):

simlet:  ...


request:
  #...


response:
  from: ...
  template: Simula

  latency:
    #...

  connection:
    close: ...
    #...

  codec:
    #...

  httpVersion: ...
  status: ...
  headers:
    #...
  body: ...

Some of the configuration fields are optional, some have sub-configuration, and others are mutually exclusive. A detailed documentation of the various configuration aspects of each field is in Response Stubbing and Response Templating.

It is to note that a single response configuration can produce different outputs when using dynamic responses via templating.

Multiple Responses

Often times a slight variation in a request has to return different response. For example, everything else being the same in a request - e.g. method is GET and route is /api/some/report - missing input query string parameters for date range would cause 400 (Bad Request) to be returned, and 200 (OK) with the actual data otherwise (see the example below).

One way to simulate such responses is to define a separate simlet for each such variation. Another could be to resort to writing scripting code.

API Simulator offers you another choice - ability to model multiple responses in the configuration for the same simlet. Here is an example:

simlet: multiple-responses-for-a-request-example


request:
- uriPath: /api/some/report
- method: GET


responses:
- when:
    request:
    - where: uriQueryParameter
      named: fromDate
      exists: false
  # then
  from: stub
  status: 400
  body: |
    {
      "status": "error",
      "message": "Missing required parameter 'fromDate'"
    }

- when:
    request:
    - where: uriQueryParameter
      named: toDate
      exists: false
  # then
  from: stub
  template: simula
  status: 400
  body: |
    {
      "status": "error",
      "message": "Missing required parameter 'toDate'"
    }

  # otherwise
- from: template
  template: simula
  status: 200
  headers:
  - "Content-Type: application/json"
  body: |
    {
      "status": "OK",
      "message": "Received request with required parameters fromDate='${_request.uri.query.params.first('fromDate')}' and toDate='${_request.uri.query.params.first('toDate')}'"
    }

It is OK if the example above may have triggered some questions about the simlet configuration - the various configuration elements are explained elsewhere in the docs. It should at least be clear that the simlet will return responses with status code 400 (Bad Request) and the response body will contain an error message in JSON when either one of the fromDate or toDate query string parameters are missing. For example, for requests like these:

  • GET /api/some/report

  • GET /api/some/report?fromDate=2020-01-01

  • GET /api/some/report?toDate=2020-01-31

(A more complete example would also check for valid dates)

More generally, to model multiple responses in the configuration for the same simlet:

simlet: ...


request:
  #...


responses:
- when:
    request:
      #...<any request matching or sampling rules>...
  # then
  from: ...
  #...<configuration for a single response>...

- when:
    request:
      #...<any request matching or sampling rules>...
  # then
  from: ...
  #...<configuration for a single response>...

  # otherwise - if none of the above match
- from: ...
  #...<configuration for a single response>...

The responses element contains a list of possible responses. Which response is returned is based on evaluating to true some condition configured in when. The order of responses in the list determines the order in which API Simulator evaluates them to decide which response to return.

The last response in the list usually does not have a when condition and it is the response that will be returned if no response in the list before it is matched.

If there is not such last response without a when condition and none of the other responses match, then the rules described in On Request Mismatch apply.

The condition in when can be any combination of matching rules described in Request Matching, plus rules for Request Sampling.

It is an error to have both response and responses elements.

Oh, yeah - the # then and # otherwise comments are not required.

Request Sampling

definition

Sampling is a process of selecting a few elements from a large collection.

In API Simulator, request sampling is a form of request matching.

Random Sampling

Random sampling uses uniform distribution to generate values. An algorithm determines which ones will be part of the sample. The sampling is approximate.

One of the main uses of random sampling in API Simulator is to simulate some of request/response exchanges failing with something like 503 (Service Unavailable). Here is an example:

simlet: random-sampling-example


request:
- uriPath: /api/path
- method: GET


responses:
- when:
    request:
    - sample: random
      percent: 2
  # then
  from: stub
  status: 503
  headers:
  - "Content-Type: application/json"
  body: |
    {
      "status": "error",
      "message": "Service Unavailable"
    }

  # otherwise
- from: stub
  status: 200
  headers:
  - "Content-Type: application/json"
  body: |
    {
      "status": "OK"
    }

About 2% of the responses will return status code 503 (Service Unavailable) and the rest - 200 (OK).

The general format is as follows:

responses:
#...
- when:
    request:
    #- ...
    - sample: random
      percent: <fractional-number-between-0.0-and-100.0>
  #...

All requests have equal weight. percent is a fractional number with boundaries between 0.0 and 100.0, inclusive:

  • percent: 10 ⇒ approximately 1 in 10 will be selected

  • percent: 50.0 ⇒ approximately half will be selected

  • percent: 67.3 ⇒ approximately 67.3 out of 100, or 673 of 1000 will be selected

Request Sequence Number Sampling

This sampling uses request selection based on the sequence number of requests that have matched a simlet. The following comparison operations are supported:

responses:
  # ...
- when:
    request:
      # The first request that matches this simlet has sequence
      # number of 1, the second has sequence number of 2, etc.
    - sample: sequence
      equals: 1
      eq: 1     # same as "equals"

      not equals: 5
      notEquals:  5 # same as "not equals"
      neq: 5        # same as "not equals"

      lessThan: 10
      lt: 10    #  same as "lessThan"

      lessThanOrEqual: 5
      lte: 5    # same as "lessThanOrEqual"

      greaterThan: 11
      gt: 11    # same as "greaterThan"

      greaterThanOrEqual: 10
      gte: 10   # same as "greaterThanOrEqual"

      in: [1, 3, 10, 22] # non-empty list of whole numbers

      not in: [1, 3, 10, 22] # non-empty list of whole numbers
      nin: [1, 3, 10, 22]    # same as "not in"

  # then
  from: ...
  #...

Sequence sampling supports more than one condition. For example (the # and comment is not required):

responses:
  #...
- when:
    request:
    - sample: sequence
      gte: 3
      # and
      lte: 5

…​results in the requests for this simlet with sequence numbers between 3 and 5, inclusive, being selected.

An example Use Case for Sequence sampling is when the response is to return 503 (Service Unavailable) for the first, say, 3 requests, and 200 (OK) for any request thereafter:

responses:
  #...
- when:
    request:
    - sample: sequence
      lte: 3
  # then
  from: stub
  status: 503

  # otherwise
- from: stub
  status: 200
  #...

Another Use Case is a simulation where the order of requests is controlled and always the same; a particular response is to be returned based on the sequence number of the request: some response #1 for request #1, another response #2 for request #2, etc.

Fixed Rate Sampling

With Fixed Rate sampling, every Nth request for the simlet will be selected.

responses:
#...
- when:
    request:
    #- ...
    - sample: fixed
      rate: <whole-positive-number>
  #...

rate: 1 is the same as taking every request. When rate is 5 then every 5th request for the simlet will be selected - 5th, 10th, 15th, 20th, etc.

Rate Limiting

Matching a simlet request for rate limiting is request sampling - simlet requests above the predefined rate of calls per second are selected. It is explained in Rate Limiting.


We would love to hear your feedback! Shoot us an email to [feedback at APISimulator.com] about anything that is on your mind.

Happy API Simulating!