API Simulation Configuration

Overview

If you have not already, make sure to read the Basic Concepts page to get you started.

Then go over the sections below to get an understanding of how to model and configure API simulations and their simlets. Find more in depth documentation on the pages that follow.

DSL for Simulation Modeling

Modeling API simulations via configuration is now easier than ever with API Simulator’s purpose-built Domain Specific Language (DSL).

The expressive DSL helps you to define simlets using simple and easy to understand constructs. Few constructs only, actually: name/value pairs, lists, and maps. Much like JSON but less verbose and easier to use for configurations. It is a small subset of YAML with a custom extension for "indentationless" multi-line strings. Don’t worry if YAML is something new to you - in no time you will be reading and writing simlets.

Here is a small example demonstrating a possible configuration for matching HTTP GET requests that have a particular URI path:

request:
- method: GET
- uriPath: /api/v2/categories

Make sure to check out the free online API Simlet Editor (beta) .

About YAML

YAML (YAML Ain’t Markup Language), per the description on its web site, is a "…​human friendly data serialization standard for all programming languages". It has gained wide acceptance as the language of choice for configuration.

YAML uses indentation-based scoping. In general, an element must be indented further than its parent element, and elements at the same level have the same indentation.

Indentation is very important in YAML. The right indentation leads to a well-formed YAML document and proper interpretation of the configuration.

Since v1.7, API Simulator extends the YAML syntax to support multi-line text content that doesn’t have to be indented. That makes configuring text elements like HTTP body content a breeze.

YAML and JSON

JSON is a subset of YAML 1.2 - a JSON document is also a YAML document.

API Simulator can load simlet configuration in JSON as well. However, the JSON-based DSL will not be documented at this time. While both YAML and JSON can be used for human-friendly representation of data structures, arguably perhaps, YAML is more suitable for configuration and JSON for messaging. For example, multi-line string values are pain to put in JSON, comments in JSON are not standard, and more…​

API Simulation Names

Each API Simulation has a name. A simulation name must be a valid subdomain name:

  • Starts with an a through z letter.

  • Then contains letters a through z, digits 0 through 9, and hyphen -, where hyphen is not the last character.

  • Has maximum length of 63.

  • Is case-insensitive.

These rules are currently not enforced; however, following them will assure backward compatibility in the future.

API Simlet Names

Each API simlet has a name. The names follow the rules for valid subdomain name like in API Simulation Names.

Creating a New Simulation

To create a new API Simulation simply create a directory and name it after the name of the simulation. See above for the rules to follow when naming it.

Next, model simulation’s simlets. API Simulator gives you a choice when it comes to organizing the simlets:

  • All-in-one configuration file.

  • Dedicated directory per simlet.

All-in-one Configuration

All simlets of a simulation can be defined in a single file named apisim.yaml and placed in the simulation’s directory:

api-simulation-name
└─ apisim.yaml

Inside apisim.yaml, simlet definitions are separated by three dashes --- on a line:

simlet: <simlet1-name>
<simlet1 definition>
---
simlet: <simlet2-name>
<simlet2 definition>
---
# ....
---
simlet: <simletN-name>
<simletN definition>

Each simlet requires a name followed by the simlet-specific configuration.

The all-in-one configuration file is suitable for small number of simlets with generally smaller response payloads. Otherwise, it becomes difficult to navigate up and down the single, potentially big file.

Directory per Simlet

Another option API Simulator supports is to have a directory per simlet, all under a directory called simlets:

api-simulation-name
└─ simlets

The configuration for each simlet is placed in a directory named after the simlet and under the simlets folder. There is always a file named simlet.yaml, which contains the simlet configuration. For example:

payments-simulation
└─ simlets
   ├─ create-payment
   │  └─ simlet.yaml
   ├─ get-payment
   │  └─ simlet.yaml
   ├─ list-payments
   │  ├─ simlet.yaml
   │  └─ large-payments-list.json
   └─ not-found
      └─ simlet.yaml

In the example above, create-payment, get-payment, list-payments, and not-found are directories for simlets with those names; each directory has a simlet.yaml file that contains the definition of that simlet. The list-payments directory also has another file that is external to simlet.yaml, presumably with the JSON body of the response.

If there is a simlet with some name in apisim.yaml and directory with that same name under simlets then the simlet from apisim.yaml will not get loaded - the one in simlets takes precedence.

Using External Files

Both options for organizing the simlets allow the HTTP response body for a simlet to be in an external file. Having a separate file helps with simlets that return binary content (e.g. images) and/or large responses.

External files are also supported for external scripts in scripting.

Aside from using absolute paths to external files, API Simulator supports the following global variables that can be used to represent relative paths to files:

  • ${sim.path} - resolves to the simulation directory.

  • ${simlets.path} - points to the simlets directory for the simulation.

  • ${simlet.path} - points to the directory from where a simlet’s configuration was loaded. This is the same as ${sim.path} for simlets defined in the all-in-one apisim.yaml file.

Here is an example:

simlet: get-favicon

request:
- method: GET
- uriPath: /favicon.ico

response:
  from: stub
  headers:
  - "Content-Type: image/png"
  body:
    type: binary
    file: "${sim.path}/favicon-16x16.png"

In the example above, the favicon-16x16.png file is expected to be under the simulation directory as given by ${sim.path}.

Full-blown Directory Structure

Regardless which approach you choose for organizing the simlets, the following optional directory structure can help with simulations that are more complex:

api-simulation-name
├─ bin
├─ config
├─ lib
├─ logs
├─ scripts
└─ simlets

Here is a description of each directory:

Directory Description

bin

This optional directory can be a convenient place for custom scripts to start and stop the simulation. Such scripts could take care, for example, of setting all simulation-specific start and stop arguments.

config

Custom configuration, like for logging, can be placed in this directory. If the directory is missing, such configs can be placed directly under the simulation’s directory. Optional.

lib

API Simulator is extensible by custom code. Any compiled artifacts placed in this directory will be automatically added to the classpath. Optional.

logs

Log files will be created in this directory, if it exists. Otherwise, the log files will be created directly in the simulation’s directory. Optional.

scripts

If one or more of the simlets use scripting to enhance the API Simulator’s capabilities, files with the scripts can be placed in the "scripts" directory. Optional.

simlets

Contains configuration for each simlet that is part of the simulation. Optional when using all-in-one simlets configuration file.

On Request Mismatch

API Simulator matches incoming requests to determine the simlet which response to return. There are two options for when a request cannot be matched:

  • Use a default simlet.

  • Forward the unmatched request.

Default Simlet

To define a default, fallback simlet, create a simlet and give it any valid simlet name; then configure it as matching any request:

request: any

response:
  from: stub
  status: 400
  body: "What are you doing?"

The response can be anything, including a dynamic response from template.

If an API simulation does not define a default simlet and a request cannot be matched, API Simulator will use its built-in default simlet. It returns 404 (Not Found) for HTTP status and "API Simulator couldn’t find a matching simlet for the request." in the body.

Request Forwarding

For request forwarding to work, API Simulator must be called as a HTTP or HTTPS proxy for the calling API client. This way, requests will be sent to API Simulator and will contain the actual destination host. If a request matches a simlet, API Simulator will return the response; if a request does not match a simlet, the request will be forwarded unchanged to the actual destination, and the response relayed back. In essence, API Simulator will be a pass-through for unmatched requests.

This functionality can be configured either in the Configuration File by specifying empty string as the name of the default simlet:

simulation: my-sim

defaultSimlet: ""
...

…​or see the Command-line Arguments for how to pass a command-line argument to configure API Simulator to forward unmatched requests.

Configuration File

Using the Configuration File is optional. It does make it easier, though, to override the default configuration settings, if needed. It also gives you a declarative way to configure your admin’s, simulator’s, and simulation’s run-time settings and (preferably) check them into source control along with the rest of the configuration instead of passing in command-line arguments at startup.

The Configuration File is the same apisim.yaml file in the simulation’s directory as for All-in-one Configuration. Create the file if you are using the "directory per simlet" approach.

Don’t forget to add the --- separator between the different configurations, including between simlets, if the file contains any.

Override all, none, or only some of the optional settings, as needed.

#----------------------------------------------------------------------
# Default configuration for the Admin HTTP Server.
# The Admin enables the management of the API Simulator
# and API Simulation(s) via API calls over HTTP/S.
# Override all, none, or only some of the settings, as needed.
# Optional.
#----------------------------------------------------------------------
admin: http

# By default, the Admin Server binds to 0.0.0.0 (anyLocalAddress) and
# it will accept connections on any of host's IP addresses (network
# interfaces). Set the host to localhost or 127.0.0.1 to restrict
# access to local clients only.
# Optional.
host: 0.0.0.0

# There is usually no need to set the port number on which the
# Admin Server will listen unless there is a conflict and/or
# more than one API Simulator instance is running on the host.
# Optional.
port: 6190


security:
  api:
    # All calls to the Admin APIs must send the API Key as well.
    # Set it to a blank or empty string value here to disable
    # the security check. The default value is "apisimulator".
    # Optional.
    key: "apisimulator"


# Admin Server TLS configuration for HTTPS connections.
# By default it uses a JKS KeyStore that comes with the distro.
# Optional.
tls:
  # Configure one of these three.
  # keyStore takes precedence over selfSignedCert, which in turn
  # takes precedence over certificate and private key in PEM format
  #keyStore:
    # Using ${sim.path} here is OK. ${simlets.path} and
    # ${simlet.path}, however, are undefined and using
    # them here will cause an error.
    #
    # Required when configuring a keyStore
    #keyStoreFile: "${sim.path}/test_apisimulator.jks"
    # Optional; if the keyStore is protected by a password
    #keyStorePassword: "storepass"
    # Optional; if the key is protected by a password
    #keyPassword: "keypass"
  # OR
  # Self-signed certificate API Simulator will create at
  # startup time and use, along with the corresponding
  # private key, to establish TLS (HTTPS) connections.
  selfSignedCert:
    # Fully-qualified domain name (FQDN) for the certificate.
    # Required when configuring a self-signed certificate
    domain: admin.apisimulator.com
  # OR
  # PEM-formatted certificate and private key
  #pem:
    # Passphrase-protected private key isn't supported. Decrypt
    # the key first. For example: openssl pkcs8 \
    # -in encrypted_private_key.key -out plain_private_key.pem
    #
    # Using ${sim.path} here is OK. ${simlets.path} and
    # ${simlet.path}, however, are undefined and using
    # them here will cause an error.
    #
    # Required when configuring PEM-formatted TLS artifacts
    #certificate: "${sim.path}/test_certificate.pem"
    # Required when configuring PEM-formatted TLS artifacts
    #privateKey: "${sim.path}/test_private_key.pem"
    # Don't specify key password here if the key is not protected by one!
    #keyPassword:


# Netty-specific server configuration
# Optional.
netty:
  bossThreads: 1
  workerThreads: 2
  readTimeoutSeconds: 3


# HTTP-specific configuration
# Optional.
http:
  request:
    codec:
      maxInitialLineBytes: 1024
      maxHeaderBytes: 8192
      maxChunkBytes: 8192
      maxContentBytes: 8388608 # 8MiB
      initialBufferBytes: 256
      validateHeaders: false


# HTTP/2 protocol-specific configuration
# Optional.
http2:
  maxConcurrentStreams: 100

  # By default, headers in HTTP/2 requests are not validated
  validateHeaders: false

  # Max body size for plain-text HTTP/1.1 requests sent to
  # the server and asking to upgrade the protocol to HTTP/2
  upgradeRequestMaxContentBytes: 8388608 # 8MiB
---
#----------------------------------------------------------------------
# Default HTTP API Simulator configuration.
# The API Simulator hosts the API Simulation.
# Override all, none, or only some of the settings, as needed.
# Optional.
#----------------------------------------------------------------------
simulator: http

# API Simulator will bind to this host to listen for connections.
# Useful when the host has multiple network interfaces. It defaults
# to 0.0.0.0 (anyLocalAddress) if not defined or is blank.
# Optional.
#host: 127.0.0.1

# API Simulator will listen on this port number for connections.
# Optional.
port: 6090


# Server TLS configuration for HTTPS connections.
# By default it uses a JKS KeyStore that comes with the distro.
# Optional.
tls:
  # Configure one of these three for a simulation.
  # keyStore takes precedence over selfSignedCert, which in turn
  # takes precedence over certificate and private key in PEM format
  #keyStore:
    # Using ${sim.path} here is OK. ${simlets.path} and
    # ${simlet.path}, however, are undefined and using
    # them here will cause an error.
    #
    # Required when configuring a keyStore.
    # On Windows, use single quotes or forward slash '/' to avoid problems
    # with backslash '\' being interpreted as starting an escape sequence
    #keyStoreFile: "classpath:apisimulator.jks"
    # Optional; if the keyStore is protected by a password
    #keyStorePassword: "storepass"
    # Optional; if the key is protected by a password
    #keyPassword: "keypass"
  # OR
  # Self-signed certificate API Simulator will create at
  # startup time and use, along with the corresponding
  # private key, to establish TLS (HTTPS) connections.
  selfSignedCert:
    # Fully-qualified domain name (FQDN) for the certificate.
    # Required when configuring a self-signed certificate
    domain: test.apisimulation.com
  # OR
  # PEM-formatted certificate and private key
  #pem:
    # Passphrase-protected private key isn't supported. Decrypt
    # the key first. For example: openssl pkcs8 \
    # -in encrypted_private_key.key -out plain_private_key.pem
    #
    # Using ${sim.path} here is OK. ${simlets.path} and
    # ${simlet.path}, however, are undefined and using
    # them here will cause an error.
    #
    # Required when configuring PEM-formatted TLS artifacts.
    # On Windows, use single quotes or forward slash '/' to avoid problems
    # with backslash '\' being interpreted as starting an escape sequence
    #certificate: "${sim.path}/tls_certificate.pem"
    # Required when configuring PEM-formatted TLS artifacts.
    # On Windows, use single quotes or forward slash '/' to avoid problems
    # with backslash '\' being interpreted as starting an escape sequence
    #privateKey: "${sim.path}/tls_private_key.pem"
    # Don't specify key password here if the key is not protected by one!
    #keyPassword: keypass

  # mTLS (aka mutual TLS, two-way TLS, two-way SSL). When client
  # authentication is required, clients must present during
  # the TLS handshake a certificate that the server trusts.
  # Optional. Otherwise one of (a), (b), or (c)
  #
  # (a) mTLS is off. This is the default.
  client:
    auth: off
  # OR
  # (b) mTLS is required and all client certs are to be trusted
  #client:
    #auth:
      #required:
        # Any certificate a client presents will be trusted regardless
        # of the Certificate Authority (CA) that issued and signed it
        #trust: all
  # OR
  # (c) mTLS is required and only client certificates signed
  # by certain Certificate Authorities will be trusted
  #client:
    #auth:
      #required:
        # Certificates clients present will be trusted when they are
        # signed by a Certificate Authority (CA), which certificate
        # (CA certificate) is in the bundles configured below
        #trust: ca-certs
        # CA cert bundles to use to verify the client certs
        #ca-certs:
          # PEM format is the only one supported for now
          #- pem:
              # base64-encoded bundle. The header
              # -----BEGIN CERTIFICATE----- and footer
              # -----END CERTIFICATE----- are required.
              # Like everywhere else, using tick characters `` works
              #bundle: `
#-----BEGIN CERTIFICATE-----
#THIS.IS.AN.EXAMPLE.janljDklUg03qMA0GCSqGSIb3DQEBCwUAMEwxCzAJBgNV
#....
#q518ILjkqPWX6CZAlun+oN7h4BZFoF0Xx69b5vCgiFkDVKqVOpE=
#-----END CERTIFICATE-----
#`
          # PEM format is the only one allowed
          #- pem:
              # File with certificates bundle in PEM format.
              # Using ${sim.path} here is OK. ${simlets.path} and
              # ${simlet.path}, however, are undefined and using
              # them here will cause an error.
              #file: ${sim.path}/<trusted-ca-certs-bundle.pem>


# Netty-specific server configuration
# Optional.
netty:
  bossThreads: 1
  workerThreads: 4
  # How long the server will wait for data to arrive before
  # timing out the read and returning "408 Request Timeout"
  readTimeoutSeconds: 3


# HTTP-specific configuration
# Optional.
http:
  request:
    codec:
      maxInitialLineBytes: 4096
      maxHeaderBytes: 8192
      maxChunkBytes: 8192
      maxContentBytes: 5242880 # 5MiB
      initialBufferBytes: 256
      validateHeaders: false


# HTTP/2 protocol-specific configuration
# Optional.
http2:
  maxConcurrentStreams: 101

  # By default, headers in HTTP/2 requests are not validated
  validateHeaders: false

  # Max body size for plain-text HTTP/1.1 requests sent to
  # the server and asking to upgrade the protocol to HTTP/2
  upgradeRequestMaxContentBytes: 5242880 # 5MiB
---
#----------------------------------------------------------------------
# Configuration for this HTTP API simulation.
# It overrides the default simulation configuration settings.
# Override all, none, or only some of the settings, as needed.
# Optional.
#----------------------------------------------------------------------
# The name is disregarded when loading simulation from a directory -
# the name of the simulation directory becomes the simulation name
# Required.
simulation: my-http-simulations-configuration

# The default simlet to use when a request can't be matched.
# Empty quoted string ("" or '') will cause unmatched requests
# to be forwarded to the host specified in the request's Host
# HTTP header. A blank value and null are disregarded
#defaultSimlet: apisimulator-simlet-404

# Default character set for text simlets.
# Set it to an Unicode encoding like "UTF-8" to have accented
# characters ('á', 'é', 'ü'...) properly returned in responses
defaultCharset: ISO-8859-1


# Proxy configuration. Optional.
# Currently used, if configured, when forwarding unmatched requests.
# There is also a form that allows setting connect timeout - see below
proxy:
    # Surround with double quotes to deal with `:` value separators.
    # [] denotes an optional part in the configuration.
    # Optional.
    http: "[user[:password]@]host:port"

    # Surround with double quotes to deal with `:` value separators.
    # [] denotes an optional part in the configuration.
    # Optional.
    https: "[user[:password]@]host:port"

    # Comma-delimited list of domains and IP addresses to exclude
    # from proxying. Do NOT use `*.` but just a dot `.` as wildcard
    # to denote "including any sub-domain".
    # Optional.
    noProxy: ".example.com,10.11.127.1"


# Rendering simlet responses is asynchronous
# and uses a separate thread pool
# Optional.
renderer:
  threads: 8
  maxPendingTasks: 500


# HTTP-specific configuration
# Optional.
http:
  request:
    # For forwarding unmatched requests to the actual destination host
    forwarder:
      connectTimeoutMillis: 2000
      readTimeoutMillis: 3000

  # Callbacks in a Request/Acknowledge/Callback message
  # exchange are async and use a separate thread pool.
  callback:
    # >= 0
    coreThreads: 0
    # 0 < maxThreads and maxThreads >= coreThreads
    maxThreads: 4

  response:
    # The settings here will apply to all simlets but can be
    # overridden per simlet, too.
    # https://apisimulator.io/docs/latest/standalone-api-simulator/response-http-codec.html
    codec:
      # HTTP Server Name header.
      # If not set, it will default to API Simulator's version
      #serverName:

      # The encoding to apply to the content and set as value in
      # "Content-Encoding" header. The content encoding, per the
      # HTTP 1.1 spec, is related to applying "gzip" or "deflate"
      # compression.
      # The encoding will be applied only if the request
      # specifies that the client does accept that encoding and
      # if the content isn't already encoded/compressed.
      contentEncoding:

      # The transfer encoding to apply to the content and set as
      # value of "Transfer-Encoding" header, if:
      # * the response doesn't already have non-empty
      #   "Transfer-Encoding" header
      # OR
      # * the response's content is compressed (i.e. gzip/deflate
      #   encoded) - then "chunked" transfer encoding is applied
      #   automatically.
      #
      # Notice that when the response has non-empty
      # "Transfer-Encoding" value then the content is expected to
      # be <i>already</i> encoded using the method specified in
      # the header's value (e.g. chunked).
      transferEncoding:

      # Flag if the connection is to be always closed
      # regardless of whether the client is requesting
      # or not 'Connection: keep-alive'
      alwaysCloseConnection: false

      maxInitialLineBytes: 1024
      maxHeaderBytes: 8192
      maxChunkBytes: 10240
      maxContentBytes: 10485760

      setDateWhenMissing: true
      setDateWhenPresent: false


# Bounded LRU KV (Key/Value) Stores for stateful simulations.
# The maximum number of elements in each Store is limited and
# reaching the maximum leads to the Least Recently Used (LRU)
# element to be removed, much like a cache.
# You don't have to declare KV Stores here unless you want
# them to have more than the default maximum number of slots.
# The default for maximum number of slots is 64.
# The name for the store can be any combination of letters,
# digits, dash, and underscore, and should not start with an
# underscore.
# KV Stores are not distributed but local to the API Simulator
# instance.
# Optional
#kvStores:
#  - name: <name-for-the-store>
#    slots: <max-count>
#  - name: <name2>
#    slots: <max-count-2>
---
#...optionally, simlets configuration if using all-in-one configuration...

See Response HTTP Codec for details related to http.response.codec settings.

Here is how to configure connect timeout as part of proxy settings:

proxy:
    http:
        server: "[user[:password]@]host:port"
        connectTimeoutMillis: 1500

    https:
        server: "[user[:password]@]host:port"
        connectTimeoutMillis: 2000

    noProxy: ".example.com,10.11.127.1"

We would love to hear your feedback - send us a quick email to [feedback at APISimulator.com]

Happy API Simulating!