request:
- method: GET
- uriPath: /api/v2/categories
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:
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
throughz
letter. -
Then contains letters
a
throughz
, digits0
through9
, 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 thesimlets
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-oneapisim.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 |
---|---|
|
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. |
|
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. |
|
API Simulator is extensible by custom code. Any compiled artifacts placed in this directory will be automatically added to the classpath. Optional. |
|
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. |
|
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. |
|
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
#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
#certificate: "${sim.path}/tls_certificate.pem"
# Required when configuring PEM-formatted TLS artifacts
#privateKey: "${sim.path}/tls_private_key.pem"
# Don't specify key password here if the key is not protected by one!
#keyPassword: keypass
# 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 to use for text simlets
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!