Why use response templates?

A static mock always returns the same body regardless of what the client sent. Response templates let the mock react to the incoming request — echoing a request field back in the response, generating a unique ID, or returning different data depending on a query parameter. This makes a single expectation cover an entire family of requests without enumerating every combination.

For example, a Mustache template can echo the requested user ID straight back in the response body. The {id} segment must also be declared under pathParameters, otherwise the path is matched literally and its value is not available to the template:

new ClientAndServer(1080)
    .when(
        request()
            .withPath("/users/{id}")
            .withPathParameter("id", "[0-9]+")
    )
    .respond(
        template(
            HttpTemplate.TemplateType.MUSTACHE,
            "{\"statusCode\": 200, \"body\": {\"id\": \"{{request.pathParameters.id.0}}\", \"name\": \"User {{request.pathParameters.id.0}}\"}}"
        )
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "path": "/users/{id}",
        "pathParameters": {
            "id": [ "[0-9]+" ]
        }
    },
    "httpResponseTemplate": {
        "template": "{\"statusCode\": 200, \"body\": {\"id\": \"{{request.pathParameters.id.0}}\", \"name\": \"User {{request.pathParameters.id.0}}\"}}",
        "templateType": "MUSTACHE"
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "path": "/users/{id}",
        "pathParameters": {
            "id": [ "[0-9]+" ]
        }
    },
    "httpResponseTemplate": {
        "template": "{\"statusCode\": 200, \"body\": {\"id\": \"{{request.pathParameters.id.0}}\", \"name\": \"User {{request.pathParameters.id.0}}\"}}",
        "templateType": "MUSTACHE"
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    'httpRequest' => {
        'path' => '/users/{id}',
        'pathParameters' => {
            'id' => [ '[0-9]+' ]
        }
    },
    'httpResponseTemplate' => {
        'template' => "{\"statusCode\": 200, \"body\": {\"id\": \"{{request.pathParameters.id.0}}\", \"name\": \"User {{request.pathParameters.id.0}}\"}}",
        'templateType' => 'MUSTACHE'
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
    "httpRequest": {
        "path": "/users/{id}",
        "pathParameters": {
            "id": [ "[0-9]+" ]
        }
    },
    "httpResponseTemplate": {
        "template": "{\"statusCode\": 200, \"body\": {\"id\": \"{{request.pathParameters.id.0}}\", \"name\": \"User {{request.pathParameters.id.0}}\"}}",
        "templateType": "MUSTACHE"
    }
}`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""path"": ""/users/{id}"",
        ""pathParameters"": {
            ""id"": [ ""[0-9]+"" ]
        }
    },
    ""httpResponseTemplate"": {
        ""template"": ""{\""statusCode\"": 200, \""body\"": {\""id\"": \""{{request.pathParameters.id.0}}\"", \""name\"": \""User {{request.pathParameters.id.0}}\""}}"",
        ""templateType"": ""MUSTACHE""
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "path": "/users/{id}",
        "pathParameters": {
            "id": [ "[0-9]+" ]
        }
    },
    "httpResponseTemplate": {
        "template": "{\"statusCode\": 200, \"body\": {\"id\": \"{{request.pathParameters.id.0}}\", \"name\": \"User {{request.pathParameters.id.0}}\"}}",
        "templateType": "MUSTACHE"
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "path": "/users/{id}",
        "pathParameters": {
            "id": [ "[0-9]+" ]
        }
    },
    "httpResponseTemplate": {
        "template": "{\"statusCode\": 200, \"body\": {\"id\": \"{{request.pathParameters.id.0}}\", \"name\": \"User {{request.pathParameters.id.0}}\"}}",
        "templateType": "MUSTACHE"
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "path": "/users/{id}",
        "pathParameters": {
            "id": [ "[0-9]+" ]
        }
    },
    "httpResponseTemplate": {
        "template": "{\"statusCode\": 200, \"body\": {\"id\": \"{{request.pathParameters.id.0}}\", \"name\": \"User {{request.pathParameters.id.0}}\"}}",
        "templateType": "MUSTACHE"
    }
}'

See REST API for full JSON specification

Templates are evaluated at request time, so the response is always fresh. See the sections below for the full template format reference, available variables, and helper objects.

The same templating also applies to streaming responses: set a templateType on an httpSseResponse or httpWebSocketResponse (or per-message on a gRPC stream response) and each event/message payload is rendered as a response template against the request, using the same variables and helpers described here. See SSE & WebSocket mocking.

 

Response Templates

MockServer supports dynamic response templates that generate responses based on request data, built-in variables, and helper objects. The following template features are supported:

  • Template Formats — three template engines, each with different trade-offs:
    • Mustache — simple syntax, conditional logic, JsonPath and XPath extraction
    • Velocity — Apache Velocity engine with conditionals, loops, math, and JSON/XML body parsing
    • JavaScript — full ES2023+ JavaScript via the GraalVM Polyglot engine (requires the graaljs Docker image variant or adding GraalVM Polyglot dependencies to the classpath)
  • Templates in JSON Initializer Files — defining templates in JSON expectation files
  • Loading Templates From Files — reference a template stored in a separate file with templateFile
  • Templating A Response Body File — render a FILE response body as a template with templateType
  • Response Template Testing — test templates locally using ResponseTemplateTester
  • Template Dry Run — render a template against a sample request on a running MockServer with PUT /mockserver/template/test, and find where a template fails
  • Request Model Variables — access request fields (method, path, headers, body, etc.) in templates
  • Multi-Value And Single Value Maps — access headers, parameters, and cookies by key, entry set, values, or key set
  • Dynamic Model Variables — timestamps, UUIDs, random integers, random bytes, and the server's host name
  • Template Parameters — reuse one template with different values passed as parameters
  • Template Helper Objects — rich helper objects available in all template formats:
    • JWT Helper — generate signed JWTs and JWKS for testing OAuth2/OIDC flows
    • Faker Helper — generate realistic fake names, addresses, emails, phone numbers, and 250+ other categories
    • String Helper — trim, capitalize, URL/Base64 encode/decode, substring, replace
    • JSON Transform Helper — merge, sort, extract fields, add/remove elements, pretty-print
    • Date Helper — date parsing, formatting, offsets, truncation, time zones, and epoch conversion
    • Calc Helper — random numbers, rounding, min/max, abs, ceil/floor, number formatting
    • Crypto Helper — MD5, SHA-1, SHA-256, SHA-512 digests and HMAC-SHA-256 signatures, all as lowercase hex
    • Regex Helper — test patterns, capture groups, and replace substrings using Java regex
    • HTML Helper — escape and unescape HTML entities
    • CSV Helper — parse CSV text into rows and format a row back to CSV
    • XPath Helper — evaluate an XPath expression against an XML document and pretty-print XML (XXE-safe)
    • YAML Helper — convert YAML to JSON and read a field from a YAML document
    • Lists Helper — pick a random value, build and join lists, and count through a range
    • System Value Helper — read an allowed environment variable or system property
  • When a Template Fails or Runs Too Long — time limits for JavaScript and Velocity, and what the client receives when a template fails
  • Security Considerations — templates cannot reach Java classes by default; grant what a template needs via javascriptAllowedClasses or velocityDisallowClassLoading
 

Template Feature Overview

The table below is a one-look index of every template feature on this page. Click the link in the Section column to jump straight to the reference.

Feature / Object What it provides Section
Template Formats
Mustache Logic-less templates with sections, loops, JsonPath, and XPath extraction Mustache Templates
Velocity Apache Velocity engine — conditionals, loops, math, JSON/XML body parsing Velocity Templates
JavaScript Full ES2023+ via GraalVM Polyglot — requires the graaljs image variant JavaScript Templates
Template Sources
Templates in JSON Files Embedding templates in JSON initializer files; newline escaping rules Templates in JSON Initializer Files
Templates from Files Reference a template stored in a file with templateFile Loading Templates From Files
Body File Templating Render a FILE response body as a Mustache or Velocity template Templating A Response Body File
Template Testing Test templates locally with ResponseTemplateTester Response Template Testing
Template Dry Run Render a template against a sample request on a running MockServer, with the line and column of any failure Template Dry Run
Request Variables
Request Model Variables Access method, path, headers, cookies, body, form fields, multipart parts, host, remoteAddress, and more Request Model Variables
Multi-Value Maps Access headers, path parameters, and query parameters by key, entry set, values, or key set Multi-Value And Single Value Maps
Built-in Dynamic Variables
Dynamic Variables Timestamps (now_iso_8601, now_epoch, now_rfc_1123), UUID, random integers, random bytes Dynamic Model Variables
Helper Objects
jwt Generate signed JWTs and JWKS for testing OAuth2/OIDC flows JWT Helper
strings Trim, capitalize, URL/Base64 encode/decode, substring, replace, length, contains String Helper
jsonTransform Merge, sort, extract fields, add/remove elements, pretty-print JSON strings JSON Transform Helper
dates Parse, format, offset and truncate dates, in any time zone, including the HTTP date format and epoch conversion Date Helper
calc Random numbers, rounding, min/max, abs, ceil/floor, number formatting Calc Helper
faker Realistic fake data — names, addresses, emails, phone numbers, 250+ categories Faker Helper
crypto MD5, SHA-1, SHA-256, SHA-512 digests and HMAC-SHA-256 signatures, all as lowercase hex Crypto Helper
regex Test patterns, capture groups, and replace substrings using Java regex Regex Helper
html Escape and unescape HTML entities (HTML 4.0) HTML Helper
csv Parse CSV text into rows and format a row back to CSV (RFC 4180) CSV Helper
xpath Evaluate an XPath expression against an XML document, and pretty-print XML (XXE-safe) XPath Helper
yaml Convert YAML to JSON and read a top-level field from a YAML document YAML Helper
lists Pick a random value, build and join lists, and count through a range Lists Helper
systemValue Read an environment variable or system property named in templateSystemValueAllowList System Value Helper
parameters Values set on the template with parameters, so one template can be reused Template Parameters
Security
Security Considerations Java class access is blocked by default; grant it via javascriptAllowedClasses / velocityDisallowClassLoading Security Considerations
 

Templates in JSON Initializer Files

When defining response templates in JSON expectation initializer files (loaded via MOCKSERVER_INITIALIZATION_JSON_PATH), newlines within template strings must be represented as \\n because JSON does not support multi-line string values. For example:

{
  "httpRequest": {
    "path": "/some/path"
  },
  "httpResponseTemplate": {
    "templateType": "MUSTACHE",
    "template": "{ 'statusCode': 200, 'body': 'line1\\nline2' }"
  }
}

Complex multi-line templates can become difficult to read and maintain in JSON format. For templates with significant logic or formatting, consider using a class-based ExpectationInitializer instead. A Java class initializer allows you to define templates as normal multi-line strings and provides full IDE support for editing and debugging.

See initializing expectations for details on both JSON and class-based initializers.

 

Loading Templates From Files

Instead of embedding the template inline, the template can be stored in a separate file and referenced with templateFile (a path resolved from the classpath or the filesystem). This keeps large templates out of the expectation JSON and lets you edit them with proper syntax highlighting. If both template and templateFile are provided, the inline template takes precedence.

{
  "httpRequest": {
    "path": "/some/path"
  },
  "httpResponseTemplate": {
    "templateType": "MUSTACHE",
    "templateFile": "templates/some_response.mustache"
  }
}

The same templateFile option is available on httpForwardTemplate for templated request forwarding.

 

Templating A Response Body File

A static response can already serve its body from a file using a FILE body (see creating expectations). To make that file dynamic, add a templateType of MUSTACHE or VELOCITY — the file contents are then rendered as a template against the request before being returned as the response body. The status code, headers, and content type still come from the static response, while only the body file is templated:

{
  "httpRequest": {
    "path": "/some/path"
  },
  "httpResponse": {
    "statusCode": 200,
    "body": {
      "type": "FILE",
      "filePath": "responses/order.json",
      "templateType": "MUSTACHE",
      "contentType": "application/json"
    }
  }
}

With the body file responses/order.json containing template placeholders, for example:

{ "method": "{{ request.method }}", "path": "{{ request.path }}" }
 

Choosing The Body File From The Request

The file path can be a template too, so one expectation can serve a different file for each request (like WireMock's templated bodyFileName). Set templatedFilePath to true alongside a templateType of MUSTACHE or VELOCITY; the path is rendered against the request with the same engine, then that file is read (and its contents rendered as described above). For example, GET /files/alice returns responses/alice.json:

{
  "httpRequest": {
    "path": "/files/{name}",
    "pathParameters": {
      "name": [ "[a-z0-9_-]+" ]
    }
  },
  "httpResponse": {
    "body": {
      "type": "FILE",
      "filePath": "responses/{{ request.pathParameters.name.0 }}.json",
      "templateType": "MUSTACHE",
      "templatedFilePath": true,
      "contentType": "application/json"
    }
  }
}

With Velocity, write the same path as responses/$!{request.pathParameters['name'][0]}.json. Use the braced ${...} form so the .json suffix is not read as part of the expression. In Java, use FileBody.file("responses/{{ request.pathParameters.name.0 }}.json", MediaType.APPLICATION_JSON, TemplateType.MUSTACHE).withTemplatedFilePath(true). The other clients take the same flag on their FILE body: Python Body.file(..., templated_file_path=True), Ruby Body.file(..., templated_file_path: true), Go BodyFromTemplatedFile(...), .NET WithTemplatedFileBody(...), Rust Body::file(...).with_templated_file_path(true), PHP fileBody($path, $contentType, 'MUSTACHE', true), and Node through the templatedFilePath JSON field. The dashboard composer offers it as an option once a template engine is chosen for a body file.

Because part of the path now comes from the request, MockServer keeps it inside one directory. That directory is the fixed part of filePath before the first template expression, up to its last / (responses/ in the example). It must name a directory: a path that starts with a template expression, or with only /, ./ or a drive such as C:/ before it, is always refused, because it would let a request read any file in the working directory, on the classpath or on the disk. The request-supplied part of the rendered path is refused if it:

  • contains a .. or . segment;
  • starts with /, or contains an empty segment (//);
  • contains \, : or %, which covers Windows paths and drive letters and URL-encoded traversal;
  • contains a control or line-separator character, including a null byte;
  • resolves through a symbolic link to a file outside the directory.

Like a literal path, a relative path is looked up on the classpath first (for example in your test resources), then on the filesystem relative to the working directory; the same rules apply to both. A classpath resource is only served from a directory or a jar; one that comes from anywhere else, such as an application server's virtual file system, is refused because MockServer cannot check where it really is.

A refused path gets the same 500 response as a file that cannot be read, and so does a rendered path whose file does not exist. The rejected path is logged on the server only.

Choosing the file this way is opt-in. Without templatedFilePath, the path is used exactly as written, even when it contains {{ or $. A path with no template syntax is also used as written. Path templating needs a request, so it applies to response bodies and not to request-body matching. A templated path also templates the file's contents, so use it for text files, not binary ones such as images.

Body-file templating uses the text-based engines (MUSTACHE and VELOCITY) because they render text directly. JavaScript templates build a full response object rather than a text fragment, so for JavaScript use an httpResponseTemplate (optionally with templateFile) instead.

A FILE body in an httpResponse action, templated or not, is read from disk by a small pool of threads of its own (shared with static files), so a slow disk delays only those responses, not other requests. If more than 1,000 file reads are waiting, further ones get 503 Service Unavailable with a Retry-After: 1 header, counted by the mock_server_overload_rejections metric with reason="file_bodies" when metrics are enabled. When MockServer runs as a WAR, the file is read on the servlet container's request thread instead.

 

Response Template Testing

To test response templates locally is it possible to use the org.mockserver.templates.ResponseTemplateTester for fast feedback and iteration

mustache templates can be tested locally as follows:

// inputs
String template = "{\n" +
    "    'statusCode': 200,\n" +
    "    'body': \"{'method': '{{ request.method }}', 'path': '{{ request.path }}', 'headers': '{{ request.headers.host.0 }}'}\"\n" +
    "}";
HttpRequest request = request()
    .withPath("/somePath")
    .withMethod("POST")
    .withHeader(HOST.toString(), "mock-server.com")
    .withBody("some_body");

// execute
HttpResponse httpResponse = ResponseTemplateTester.testMustacheTemplate(template, request);

// result
System.out.println("httpResponse = " + httpResponse);

velocity templates can be tested locally as follows:

// inputs
String template = "{\n" +
    "    'statusCode': 200,\n" +
    "    'body': \"{'method': '$request.method', 'path': '$request.path', 'headers': '$request.headers.host[0]'}\"\n" +
    "}";
HttpRequest request = request()
    .withPath("/somePath")
    .withMethod("POST")
    .withHeader(HOST.toString(), "mock-server.com")
    .withBody("some_body");

// execute
HttpResponse httpResponse = ResponseTemplateTester.testVelocityTemplate(template, request);

// result
System.out.println("httpResponse = " + httpResponse);

javascript templates can be tested locally as follows:

// inputs
String template = "return {\n" +
    "    'statusCode': 200,\n" +
    "    'body': '{\\'method\\': \\'' + request.method + '\\', \\'path\\': \\'' + request.path + '\\', \\'headers\\': \\'' + request.headers.host[0] + '\\'}'\n" +
    "};";
HttpRequest request = request()
    .withPath("/somePath")
    .withMethod("POST")
    .withHeader(HOST.toString(), "mock-server.com")
    .withBody("some_body");

// execute
HttpResponse httpResponse = ResponseTemplateTester.testJavaScriptTemplate(template, request);

// result
System.out.println("httpResponse = " + httpResponse);
 

Template Dry Run

To check a template without creating an expectation, send it to a running MockServer with PUT /mockserver/template/test. MockServer renders it against a sample request, with the same template engines, security settings and JavaScript timeout it uses when serving, and tells you what it rendered or why it failed.

curl -X PUT http://localhost:1080/mockserver/template/test -d '{
    "templateType": "VELOCITY",
    "template": "{\n  \"statusCode\": 200,\n  \"body\": \"hello $request.queryStringParameters[\u0027name\u0027][0] from $request.heders\"\n}",
    "request": {
        "method": "GET",
        "path": "/hello",
        "queryStringParameters": { "name": [ "world" ] }
    }
}'

The body takes:

  • templateType — VELOCITY, MUSTACHE or JAVASCRIPT
  • template — the template, exactly as it would appear in an httpResponseTemplate or httpForwardTemplate
  • actionType — optional: RESPONSE_TEMPLATE (the default) for a response template, or FORWARD_TEMPLATE for a template that builds the request to forward
  • request — optional: the sample request, in the same format as an expectation's httpRequest; without it the template sees GET /

The response says whether the template rendered a usable response, and why not if it did not:

{
  "templateType" : "VELOCITY",
  "actionType" : "RESPONSE_TEMPLATE",
  "durationMillis" : 3,
  "valid" : true,
  "renderedResponse" : {
    "statusCode" : 200,
    "body" : "hello world from $request.heders"
  },
  "warnings" : [ {
    "message" : "$request.heders did not resolve, so it was rendered as written (write $!{...} where a missing value should render nothing)",
    "line" : 3,
    "column" : 65
  } ]
}
  • valid — true when the template rendered a valid response (or request, for a forward template); when serving, MockServer answers an invalid response or forward template with a 500, or, with templateFailureAsServerError=false, an invalid response template with a 404 and an invalid forward template with a 502
  • renderedResponse or renderedRequest — what the template rendered
  • renderedOutput — the raw rendered text, when it is not a valid response or request; error.validationErrors then says what is wrong with it
  • error — why the template failed: the engine's message, the template line and column where the engine reports them (Mustache reports a line only), and a hint for common mistakes such as an undefined JavaScript variable or a field read from a header the request does not have
  • warnings — things that did not fail the template but are probably mistakes: a Velocity reference that did not resolve and was rendered as written, a Mustache variable that did not resolve and so rendered as empty text (such as the typo {{request.pth}}; reported with its line, at most 20 per dry run), or a scenario.set(...) call, which a dry run reports instead of applying
  • timedOut — true when the template ran too long
  • a field ending in Truncated, such as messageTruncated — true when that text was cut short: messages are kept to 8 KB, warnings to 1 KB and rendered output to 64 KB

A dry run does not change scenario state and does not appear in the event log or dashboard. A JavaScript template is stopped after javascriptTemplateExecutionTimeout and a Velocity template after velocityTemplateExecutionTimeout, each after 5 seconds even when it is 0. A Mustache template, or a Velocity template busy inside one long Java call, cannot be stopped, so MockServer answers once the time limit plus 5 seconds has passed and the template keeps running. At most two dry runs run at once: a third gets a 429 until one finishes. When control plane authorisation is on, a dry run needs the mutate role, because it runs the template you send.

In the dashboard, the Mocks view's response template and forward template steps have a Test Template button that runs a dry run of the template you are writing against a recently received request.

An AI assistant connected to MockServer's MCP server can run the same dry run with the test_template tool, which needs the same role.

When a template fails while serving, the same position appears in the log: Velocity and Mustache messages already give the line (Velocity as [line 3, column 7], Mustache as @ line 4), and a JavaScript failure adds at template line 2, column 29, counted from the first line of your template, followed by a hint when one applies.

 

Request Model Variables

All templates formats have access to the following request fields

  • method
    mustache: {{ request.method }}
    velocity: $!request.method
    javascript: request.method
  • path
    mustache: {{ request.path }}
    velocity: $!request.path
    javascript: request.path
  • path parameters  (see below for multi-value map access patterns)
    mustache: {{ request.pathParameters.<key>.<index> }}
    velocity: $!request.pathParameters[<key>][<index>]
    javascript: request.pathParameters[<key>][<index>]
  • path regex capture groups  (when the expectation matched the request path with a regular expression containing capture groups; pathGroups is 1‑based, so index 0 is the whole match and index 1 is the first capture group, and is empty when the matched path had no capture groups. Path‑parameter {name} captures are exposed via pathParameters instead, so a path mixing {name} templates with regex (...) groups does not populate pathGroups.)
    mustache: {{ request.pathGroups.<index> }}
    velocity: $!request.pathGroups[<index>]
    javascript: request.pathGroups[<index>]
  • named path regex capture groups  (Java named groups (?<name>...) in the matched path regex, keyed by name; empty when the matched path had no named groups)
    mustache: {{ request.namedPathGroups.<name> }}
    velocity: $!request.namedPathGroups.<name>
    javascript: request.namedPathGroups.<name>
  • query string parameters  (see below for multi-value map access patterns)
    mustache: {{ request.queryStringParameters.<key>.<index> }}
    velocity: $!request.queryStringParameters[<key>][<index>]
    javascript: request.queryStringParameters[<key>][<index>]
  • headers  (see below for multi-value map access patterns)
    mustache: {{ request.headers.<key>.<index> }}
    velocity: $!request.headers[<key>][<index>]
    javascript: request.headers[<key>][<index>]

    Header name lookup is case-insensitive, as HTTP header names are. Any casing in the template resolves any casing the client sent on the wire — for example request.headers.host, request.headers.Host and request.headers.HOST all read the same header, whether the request arrived over HTTP/1.1 (which keeps the client's casing) or HTTP/2 (which lower-cases header names). This applies to headers only; cookie, query-string-parameter and path-parameter names remain case-sensitive.

  • cookies  (see below for single-value map access patterns)
    mustache: {{ request.cookies.<key> }}
    velocity: $!request.cookies[<key>]
    javascript: request.cookies[<key>]
  • body

    request.body returns the raw request body as a string. To access individual fields within a JSON body, use JsonPath (Mustache), $json.parse() (Velocity), or JSON.parse() (JavaScript) as shown in the examples below.

    A binary body is given as base64 text, here, in callbacks and in WASM rules. That includes a body sent without a Content-Type header that is not valid UTF-8 (for example text in another character set). To receive such a body as text, send a Content-Type header with a charset, for example text/plain; charset=ISO-8859-1.

    mustache: {{ request.body }}
    velocity: $!request.body
    javascript: request.body
  • secure
    mustache: {{ request.secure }}
    velocity: $!request.secure
    javascript: request.secure
  • client certificate chain

    During TLS connections MockServer requests clients to optionally present certificates i.e. optional mTLS.
    If the client presents certificates this field is populated with a list of the clients certificates.
    Each item in the list contains the following fields:

    • issuerDistinguishedName as a string
    • subjectDistinguishedName as a string
    • serialNumber as a string
    • signatureAlgorithmName as a string
    • certificate as a java.security.cert.X509Certificate
    mustache: {{ request.clientCertificateChain }}
    velocity: $!request.clientCertificateChain
    javascript: request.clientCertificateChain
  • remote address

    The address of the TCP connection to MockServer; typically the client's address, unless there is a NAT device or load balancer between the client and MockServer.

    mustache: {{ request.remoteAddress }}
    velocity: $!request.remoteAddress
    javascript: request.remoteAddress
  • keep alive

    True is the client requested HTTP keep alive.

    mustache: {{ request.keepAlive }}
    velocity: $!request.keepAlive
    javascript: request.keepAlive
  • request id

    A unique id for this request; the same id MockServer records on the request’s log entries, so a response can be traced back to the log.

    mustache: {{ request.id }}
    velocity: $!request.id
    javascript: request.id
  • scheme

    https when the request arrived over TLS, otherwise http.

    mustache: {{ request.scheme }}
    velocity: $!request.scheme
    javascript: request.scheme
  • host

    The host the client addressed, taken from the Host header (for HTTP/2 and HTTP/3, the :authority); when there is no Host header, the local address the request arrived on.

    mustache: {{ request.host }}
    velocity: $!request.host
    javascript: request.host
  • port

    The port the client addressed: the Host header’s port, or 443 / 80 for https / http when the header has no port; when there is no Host header, the local port the request arrived on.

    mustache: {{ request.port }}
    velocity: $!request.port
    javascript: request.port
  • base url

    The scheme and authority the client used, such as https://api.example.com:8443, handy for building absolute Location headers or links. host, port and baseUrl come from the Host header, which the client sets, so treat them as client input and do not rely on them for anything security-sensitive. Forwarding headers such as X-Forwarded-Host and X-Forwarded-Proto are not used, so behind a TLS-terminating proxy scheme is http.

    mustache: {{ request.baseUrl }}
    velocity: $!request.baseUrl
    javascript: request.baseUrl
  • body as base64

    The raw request body bytes, base64 encoded; useful for echoing a binary body. Empty when there is no body.

    mustache: {{ request.bodyAsBase64 }}
    velocity: $!request.bodyAsBase64
    javascript: request.bodyAsBase64
  • form data

    The fields of an application/x-www-form-urlencoded body, as a multi-value map (see below for access patterns). Empty for any other body. Up to 1,000 fields are read, and a body larger than 10 MiB is not read; either logs a warning, which a dry run reports. A body that cannot be decoded gives an empty map.

    mustache: {{ request.formData.<name>.<index> }}
    velocity: $!request.formData[<name>][<index>]
    javascript: request.formData[<name>][<index>]
  • multipart parts

    The parts of a multipart/form-data body, as a map from part name to a list of parts (a name can repeat, for example several files). Each part has name, filename, contentType, headers (its Content-Disposition and Content-Type), body and bodyAsBase64. Empty for any other body. A part’s body and bodyAsBase64 are only built when read, so reading just the filename of a large upload does not keep a copy of each part's content (the body is still decoded once, temporarily, to find the parts). A body larger than 10 MiB (a fixed limit, separate from mockserver.maxRequestBodySize) or with more than 100 parts gives an empty map and logs a warning, which a dry run reports; a body that cannot be decoded gives an empty map.

    mustache: {{ request.parts.<name>.<index>.body }}
    velocity: $!request.parts[<name>][<index>].body
    javascript: request.parts[<name>][<index>].body

MockServer works out these fields only when a template reads them, so templates that do not use them, and responses that are not templated, cost nothing extra. In JavaScript they are not listed by Object.keys(request) or included in JSON.stringify(request), but can be read directly.

The examples below answer a multipart/form-data upload that has a title field and a file part, with a Location header built from the request’s base URL and the uploaded filename, an X-Request-Id header and a body such as stored notes.txt titled hello:

new ClientAndServer(1080)
    .when(request().withPath("/upload"))
    .respond(
        template(
            HttpTemplate.TemplateType.MUSTACHE,
            "{ 'statusCode': 201, 'headers': { 'Location': [ '{{ request.baseUrl }}/uploads/{{ request.parts.file.0.filename }}' ], 'X-Request-Id': [ '{{ request.id }}' ] }, 'body': 'stored {{ request.parts.file.0.filename }} titled {{ request.parts.title.0.body }}' }"
        )
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "path": "/upload"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 201, 'headers': { 'Location': [ '{{ request.baseUrl }}/uploads/{{ request.parts.file.0.filename }}' ], 'X-Request-Id': [ '{{ request.id }}' ] }, 'body': 'stored {{ request.parts.file.0.filename }} titled {{ request.parts.title.0.body }}' }",
        "templateType": "MUSTACHE"
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "path": "/upload"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 201, 'headers': { 'Location': [ '{{ request.baseUrl }}/uploads/{{ request.parts.file.0.filename }}' ], 'X-Request-Id': [ '{{ request.id }}' ] }, 'body': 'stored {{ request.parts.file.0.filename }} titled {{ request.parts.title.0.body }}' }",
        "templateType": "MUSTACHE"
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    'httpRequest' => {
        'path' => '/upload'
    },
    'httpResponseTemplate' => {
        'template' => "{ 'statusCode': 201, 'headers': { 'Location': [ '{{ request.baseUrl }}/uploads/{{ request.parts.file.0.filename }}' ], 'X-Request-Id': [ '{{ request.id }}' ] }, 'body': 'stored {{ request.parts.file.0.filename }} titled {{ request.parts.title.0.body }}' }",
        'templateType' => 'MUSTACHE'
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
    "httpRequest": {
        "path": "/upload"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 201, 'headers': { 'Location': [ '{{ request.baseUrl }}/uploads/{{ request.parts.file.0.filename }}' ], 'X-Request-Id': [ '{{ request.id }}' ] }, 'body': 'stored {{ request.parts.file.0.filename }} titled {{ request.parts.title.0.body }}' }",
        "templateType": "MUSTACHE"
    }
}`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""path"": ""/upload""
    },
    ""httpResponseTemplate"": {
        ""template"": ""{ 'statusCode': 201, 'headers': { 'Location': [ '{{ request.baseUrl }}/uploads/{{ request.parts.file.0.filename }}' ], 'X-Request-Id': [ '{{ request.id }}' ] }, 'body': 'stored {{ request.parts.file.0.filename }} titled {{ request.parts.title.0.body }}' }"",
        ""templateType"": ""MUSTACHE""
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "path": "/upload"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 201, 'headers': { 'Location': [ '{{ request.baseUrl }}/uploads/{{ request.parts.file.0.filename }}' ], 'X-Request-Id': [ '{{ request.id }}' ] }, 'body': 'stored {{ request.parts.file.0.filename }} titled {{ request.parts.title.0.body }}' }",
        "templateType": "MUSTACHE"
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "path": "/upload"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 201, 'headers': { 'Location': [ '{{ request.baseUrl }}/uploads/{{ request.parts.file.0.filename }}' ], 'X-Request-Id': [ '{{ request.id }}' ] }, 'body': 'stored {{ request.parts.file.0.filename }} titled {{ request.parts.title.0.body }}' }",
        "templateType": "MUSTACHE"
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "path": "/upload"
    },
    "httpResponseTemplate": {
        "template": "{ '\''statusCode'\'': 201, '\''headers'\'': { '\''Location'\'': [ '\''{{ request.baseUrl }}/uploads/{{ request.parts.file.0.filename }}'\'' ], '\''X-Request-Id'\'': [ '\''{{ request.id }}'\'' ] }, '\''body'\'': '\''stored {{ request.parts.file.0.filename }} titled {{ request.parts.title.0.body }}'\'' }",
        "templateType": "MUSTACHE"
    }
}'

See REST API for full JSON specification

new ClientAndServer(1080)
    .when(request().withPath("/upload"))
    .respond(
        template(
            HttpTemplate.TemplateType.VELOCITY,
            "{ 'statusCode': 201, 'headers': { 'Location': [ '$!request.baseUrl/uploads/$!request.parts.file[0].filename' ], 'X-Request-Id': [ '$!request.id' ] }, 'body': 'stored $!request.parts.file[0].filename titled $!request.parts.title[0].body' }"
        )
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "path": "/upload"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 201, 'headers': { 'Location': [ '$!request.baseUrl/uploads/$!request.parts.file[0].filename' ], 'X-Request-Id': [ '$!request.id' ] }, 'body': 'stored $!request.parts.file[0].filename titled $!request.parts.title[0].body' }",
        "templateType": "VELOCITY"
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "path": "/upload"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 201, 'headers': { 'Location': [ '$!request.baseUrl/uploads/$!request.parts.file[0].filename' ], 'X-Request-Id': [ '$!request.id' ] }, 'body': 'stored $!request.parts.file[0].filename titled $!request.parts.title[0].body' }",
        "templateType": "VELOCITY"
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    'httpRequest' => {
        'path' => '/upload'
    },
    'httpResponseTemplate' => {
        'template' => "{ 'statusCode': 201, 'headers': { 'Location': [ '$!request.baseUrl/uploads/$!request.parts.file[0].filename' ], 'X-Request-Id': [ '$!request.id' ] }, 'body': 'stored $!request.parts.file[0].filename titled $!request.parts.title[0].body' }",
        'templateType' => 'VELOCITY'
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
    "httpRequest": {
        "path": "/upload"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 201, 'headers': { 'Location': [ '$!request.baseUrl/uploads/$!request.parts.file[0].filename' ], 'X-Request-Id': [ '$!request.id' ] }, 'body': 'stored $!request.parts.file[0].filename titled $!request.parts.title[0].body' }",
        "templateType": "VELOCITY"
    }
}`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""path"": ""/upload""
    },
    ""httpResponseTemplate"": {
        ""template"": ""{ 'statusCode': 201, 'headers': { 'Location': [ '$!request.baseUrl/uploads/$!request.parts.file[0].filename' ], 'X-Request-Id': [ '$!request.id' ] }, 'body': 'stored $!request.parts.file[0].filename titled $!request.parts.title[0].body' }"",
        ""templateType"": ""VELOCITY""
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "path": "/upload"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 201, 'headers': { 'Location': [ '$!request.baseUrl/uploads/$!request.parts.file[0].filename' ], 'X-Request-Id': [ '$!request.id' ] }, 'body': 'stored $!request.parts.file[0].filename titled $!request.parts.title[0].body' }",
        "templateType": "VELOCITY"
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "path": "/upload"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 201, 'headers': { 'Location': [ '$!request.baseUrl/uploads/$!request.parts.file[0].filename' ], 'X-Request-Id': [ '$!request.id' ] }, 'body': 'stored $!request.parts.file[0].filename titled $!request.parts.title[0].body' }",
        "templateType": "VELOCITY"
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "path": "/upload"
    },
    "httpResponseTemplate": {
        "template": "{ '\''statusCode'\'': 201, '\''headers'\'': { '\''Location'\'': [ '\''$!request.baseUrl/uploads/$!request.parts.file[0].filename'\'' ], '\''X-Request-Id'\'': [ '\''$!request.id'\'' ] }, '\''body'\'': '\''stored $!request.parts.file[0].filename titled $!request.parts.title[0].body'\'' }",
        "templateType": "VELOCITY"
    }
}'

See REST API for full JSON specification

new ClientAndServer(1080)
    .when(request().withPath("/upload"))
    .respond(
        template(
            HttpTemplate.TemplateType.JAVASCRIPT,
            "return { 'statusCode': 201, 'headers': { 'Location': [ request.baseUrl + '/uploads/' + request.parts.file[0].filename ], 'X-Request-Id': [ request.id ] }, 'body': 'stored ' + request.parts.file[0].filename + ' titled ' + request.parts.title[0].body };"
        )
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "path": "/upload"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 201, 'headers': { 'Location': [ request.baseUrl + '/uploads/' + request.parts.file[0].filename ], 'X-Request-Id': [ request.id ] }, 'body': 'stored ' + request.parts.file[0].filename + ' titled ' + request.parts.title[0].body };",
        "templateType": "JAVASCRIPT"
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "path": "/upload"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 201, 'headers': { 'Location': [ request.baseUrl + '/uploads/' + request.parts.file[0].filename ], 'X-Request-Id': [ request.id ] }, 'body': 'stored ' + request.parts.file[0].filename + ' titled ' + request.parts.title[0].body };",
        "templateType": "JAVASCRIPT"
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    'httpRequest' => {
        'path' => '/upload'
    },
    'httpResponseTemplate' => {
        'template' => "return { 'statusCode': 201, 'headers': { 'Location': [ request.baseUrl + '/uploads/' + request.parts.file[0].filename ], 'X-Request-Id': [ request.id ] }, 'body': 'stored ' + request.parts.file[0].filename + ' titled ' + request.parts.title[0].body };",
        'templateType' => 'JAVASCRIPT'
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
    "httpRequest": {
        "path": "/upload"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 201, 'headers': { 'Location': [ request.baseUrl + '/uploads/' + request.parts.file[0].filename ], 'X-Request-Id': [ request.id ] }, 'body': 'stored ' + request.parts.file[0].filename + ' titled ' + request.parts.title[0].body };",
        "templateType": "JAVASCRIPT"
    }
}`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""path"": ""/upload""
    },
    ""httpResponseTemplate"": {
        ""template"": ""return { 'statusCode': 201, 'headers': { 'Location': [ request.baseUrl + '/uploads/' + request.parts.file[0].filename ], 'X-Request-Id': [ request.id ] }, 'body': 'stored ' + request.parts.file[0].filename + ' titled ' + request.parts.title[0].body };"",
        ""templateType"": ""JAVASCRIPT""
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "path": "/upload"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 201, 'headers': { 'Location': [ request.baseUrl + '/uploads/' + request.parts.file[0].filename ], 'X-Request-Id': [ request.id ] }, 'body': 'stored ' + request.parts.file[0].filename + ' titled ' + request.parts.title[0].body };",
        "templateType": "JAVASCRIPT"
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "path": "/upload"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 201, 'headers': { 'Location': [ request.baseUrl + '/uploads/' + request.parts.file[0].filename ], 'X-Request-Id': [ request.id ] }, 'body': 'stored ' + request.parts.file[0].filename + ' titled ' + request.parts.title[0].body };",
        "templateType": "JAVASCRIPT"
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "path": "/upload"
    },
    "httpResponseTemplate": {
        "template": "return { '\''statusCode'\'': 201, '\''headers'\'': { '\''Location'\'': [ request.baseUrl + '\''/uploads/'\'' + request.parts.file[0].filename ], '\''X-Request-Id'\'': [ request.id ] }, '\''body'\'': '\''stored '\'' + request.parts.file[0].filename + '\'' titled '\'' + request.parts.title[0].body };",
        "templateType": "JAVASCRIPT"
    }
}'

See REST API for full JSON specification

 

Request Multi-Value And Single Value Maps

The request variable contains the follow multi-value maps fields:

  • request.headers
  • request.pathParameters
  • request.queryStringParameters
  • request.formData

The request variable contains the follow single-value maps fields:

  • request.cookies

These multi-value or single-value maps can be accessed in the following ways:

  • values by key
    mustache: {{ request.headers.<key>.<index> }}
    {{ request.queryStringParameters.<key>.<index> }}
    {{ request.headers.<key>.<index> }}
    velocity: $!request.pathParameters[<key>][<index>]
    $!request.queryStringParameters[<key>][<index>]
    $!request.headers[<key>][<index>]
    javascript: request.pathParameters[<key>][<index>]
    request.queryStringParameters[<key>][<index>]
    request.headers[<key>][<index>]
  • entries as set

    each entry has a key and a value field
    for multi-value maps the value is a list
    for single-value maps the value is a string

    mustache: {{ request.pathParameters.entrySet }}
    {{ request.queryStringParameters.entrySet }}
    {{ request.headers.entrySet }}
    velocity: $!request.pathParameters.entrySet
    $!request.queryStringParameters.entrySet
    $!request.headers.entrySet
    javascript: not available
  • values as list
    mustache: {{ request.pathParameters.values }}
    {{ request.queryStringParameters.values }}
    {{ request.headers.values }}
    velocity: $!request.pathParameters.values
    $!request.queryStringParameters.values
    $!request.headers.values
    javascript: for (pathParameterKey in request.pathParameters) { var values = request.pathParameters[pathParameterKey]; }
    for (queryStringParameterKey in request.queryStringParameters) { var values = request.queryStringParameters[queryStringParameterKey]; }
    for (headerKey in request.headers) { var values = request.headers[headerKey]; }
  • keys as set
    mustache: {{ request.pathParameters.keySet }}
    {{ request.queryStringParameters.keySet }}
    {{ request.headers.keySet }}
    velocity: $!request.pathParameters.keySet
    $!request.queryStringParameters.keySet
    $!request.headers.keySet
    javascript: for (pathParameterKey in request.pathParameters) { // ... }
    for (queryStringParameterKey in request.queryStringParameters) { // ... }
    for (headerKey in request.headers) { // ... }
 

Dynamic Model Variables

All templates formats also have access to the following built-in dynamic variables. They return unique values each time they are used.

Tip: for deterministic testing, you can freeze or control the server clock so that date/time variables (now_iso_8601, now_epoch, now_rfc_1123) and the dates helper return predictable values.

  • date & time in ISO-8601 i.e. 2011-12-03T10:15:30Z
    mustache: {{ now_iso_8601 }}
    velocity: $!now_iso_8601
    javascript: now_iso_8601
  • unix epoch, the number of seconds that have elapsed since January 1, 1970
    mustache: {{ now_epoch }}
    velocity: $!now_epoch
    javascript: now_epoch
  • date & time in RFC 1123 i.e. Tue, 3 Jun 2008 11:05:30 GMT
    mustache: {{ now_rfc_1123 }}
    velocity: $!now_rfc_1123
    javascript: now_rfc_1123
  • uuid as string
    mustache: {{ uuid }}
    velocity: $!uuid
    javascript: uuid
  • random integer between 0 and 9 inclusive
    mustache: {{ rand_int_10 }}
    velocity: $!rand_int_10
    javascript: rand_int_10
  • random integer between 0 and 99 inclusive
    mustache: {{ rand_int_100 }}
    velocity: $!rand_int_100
    javascript: rand_int_100
  • random data 16 bytes long
    mustache: {{ rand_bytes_16 }}
    velocity: $!rand_bytes_16
    javascript: rand_bytes_16
  • random data 32 bytes long
    mustache: {{ rand_bytes_32 }}
    velocity: $!rand_bytes_32
    javascript: rand_bytes_32
  • random data 64 bytes long
    mustache: {{ rand_bytes_64 }}
    velocity: $!rand_bytes_64
    javascript: rand_bytes_64
  • random data 128 bytes long
    mustache: {{ rand_bytes_128 }}
    velocity: $!rand_bytes_128
    javascript: rand_bytes_128
  • the host name of the machine MockServer runs on: the HOSTNAME environment variable when it is set, as it is in a container or Kubernetes pod, otherwise the machine's name, looked up in the background when templates are first used (localhost until that finishes). This is the server's internal name, such as the pod or container name, and any client that calls an expectation using it sees it, so do not use it where that name should stay private
    mustache: {{ hostname }}
    velocity: $!hostname
    javascript: hostname
 

Template Parameters

A template can carry its own parameters: a JSON object of values the template reads as parameters. This lets one template be reused with different values, such as a greeting, a list of names or a limit, without editing the template text. A value can be text, a number, a boolean, a list or an object. This is the equivalent of WireMock's transformerParameters.

  • Mustache: {{ parameters.greeting }}, and a list in a section, as in {{#parameters.names}}{{.}}{{/parameters.names}}
  • Velocity: $parameters.greeting or $parameters.names[0]
  • JavaScript: parameters.greeting or parameters.names.length; parameters is a plain JavaScript object

Parameters work in response templates, forward templates, the response template of an overridden forward, and webhook templates. A template cannot change its parameters. A template without parameters has no parameters variable, so in Velocity write $!parameters.greeting where a missing value should render nothing. In Java, set them with .withParameter("greeting", "hello") or .withParameters(map); the other clients have the same field or setter: .NET WithParameter, Go Parameter, Node parameters, Python and Ruby parameters or with_parameter, PHP withParameter, and Rust Expectation::respond_template_with_parameters. The examples below send the expectation as JSON, which every client accepts.

The examples below return a body such as hello bob, pages 1,2,3, where the name is picked at random from the names parameter, and an X-Served-By header with the host name of the MockServer machine (which shows callers the server's internal name, see hostname):

new ClientAndServer(1080)
    .when(request().withPath("/greeting"))
    .respond(
        template(
            HttpTemplate.TemplateType.MUSTACHE,
            "{ 'statusCode': 200, 'headers': { 'X-Served-By': [ '{{ hostname }}' ] }, 'body': '{{ parameters.greeting }} {{#lists.pickRandom}}{{#parameters.names}}{{.}}|{{/parameters.names}}{{/lists.pickRandom}}, pages {{#lists.range.1.3}}{{.}}{{^-last}},{{/-last}}{{/lists.range.1.3}}' }"
        )
            .withParameter("greeting", "hello")
            .withParameter("names", Arrays.asList("ann", "bob"))
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "path": "/greeting"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'X-Served-By': [ '{{ hostname }}' ] }, 'body': '{{ parameters.greeting }} {{#lists.pickRandom}}{{#parameters.names}}{{.}}|{{/parameters.names}}{{/lists.pickRandom}}, pages {{#lists.range.1.3}}{{.}}{{^-last}},{{/-last}}{{/lists.range.1.3}}' }",
        "templateType": "MUSTACHE",
        "parameters": {
            "greeting": "hello",
            "names": [ "ann", "bob" ]
        }
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

import requests

requests.put("http://localhost:1080/mockserver/expectation", json={
    "httpRequest": {
        "path": "/greeting"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'X-Served-By': [ '{{ hostname }}' ] }, 'body': '{{ parameters.greeting }} {{#lists.pickRandom}}{{#parameters.names}}{{.}}|{{/parameters.names}}{{/lists.pickRandom}}, pages {{#lists.range.1.3}}{{.}}{{^-last}},{{/-last}}{{/lists.range.1.3}}' }",
        "templateType": "MUSTACHE",
        "parameters": {
            "greeting": "hello",
            "names": [ "ann", "bob" ]
        }
    }
})
require 'net/http'

uri = URI('http://localhost:1080/mockserver/expectation')
Net::HTTP.start(uri.host, uri.port) do |http|
  http.send_request('PUT', uri.path, <<~'JSON', 'Content-Type' => 'application/json')
    {
        "httpRequest": {
            "path": "/greeting"
        },
        "httpResponseTemplate": {
            "template": "{ 'statusCode': 200, 'headers': { 'X-Served-By': [ '{{ hostname }}' ] }, 'body': '{{ parameters.greeting }} {{#lists.pickRandom}}{{#parameters.names}}{{.}}|{{/parameters.names}}{{/lists.pickRandom}}, pages {{#lists.range.1.3}}{{.}}{{^-last}},{{/-last}}{{/lists.range.1.3}}' }",
            "templateType": "MUSTACHE",
            "parameters": {
                "greeting": "hello",
                "names": [ "ann", "bob" ]
            }
        }
    }
  JSON
end
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
    "httpRequest": {
        "path": "/greeting"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'X-Served-By': [ '{{ hostname }}' ] }, 'body': '{{ parameters.greeting }} {{#lists.pickRandom}}{{#parameters.names}}{{.}}|{{/parameters.names}}{{/lists.pickRandom}}, pages {{#lists.range.1.3}}{{.}}{{^-last}},{{/-last}}{{/lists.range.1.3}}' }",
        "templateType": "MUSTACHE",
        "parameters": {
            "greeting": "hello",
            "names": [ "ann", "bob" ]
        }
    }
}`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""path"": ""/greeting""
    },
    ""httpResponseTemplate"": {
        ""template"": ""{ 'statusCode': 200, 'headers': { 'X-Served-By': [ '{{ hostname }}' ] }, 'body': '{{ parameters.greeting }} {{#lists.pickRandom}}{{#parameters.names}}{{.}}|{{/parameters.names}}{{/lists.pickRandom}}, pages {{#lists.range.1.3}}{{.}}{{^-last}},{{/-last}}{{/lists.range.1.3}}' }"",
        ""templateType"": ""MUSTACHE"",
        ""parameters"": {
            ""greeting"": ""hello"",
            ""names"": [ ""ann"", ""bob"" ]
        }
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "path": "/greeting"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'X-Served-By': [ '{{ hostname }}' ] }, 'body': '{{ parameters.greeting }} {{#lists.pickRandom}}{{#parameters.names}}{{.}}|{{/parameters.names}}{{/lists.pickRandom}}, pages {{#lists.range.1.3}}{{.}}{{^-last}},{{/-last}}{{/lists.range.1.3}}' }",
        "templateType": "MUSTACHE",
        "parameters": {
            "greeting": "hello",
            "names": [ "ann", "bob" ]
        }
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "path": "/greeting"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'X-Served-By': [ '{{ hostname }}' ] }, 'body': '{{ parameters.greeting }} {{#lists.pickRandom}}{{#parameters.names}}{{.}}|{{/parameters.names}}{{/lists.pickRandom}}, pages {{#lists.range.1.3}}{{.}}{{^-last}},{{/-last}}{{/lists.range.1.3}}' }",
        "templateType": "MUSTACHE",
        "parameters": {
            "greeting": "hello",
            "names": [ "ann", "bob" ]
        }
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "path": "/greeting"
    },
    "httpResponseTemplate": {
        "template": "{ '\''statusCode'\'': 200, '\''headers'\'': { '\''X-Served-By'\'': [ '\''{{ hostname }}'\'' ] }, '\''body'\'': '\''{{ parameters.greeting }} {{#lists.pickRandom}}{{#parameters.names}}{{.}}|{{/parameters.names}}{{/lists.pickRandom}}, pages {{#lists.range.1.3}}{{.}}{{^-last}},{{/-last}}{{/lists.range.1.3}}'\'' }",
        "templateType": "MUSTACHE",
        "parameters": {
            "greeting": "hello",
            "names": [ "ann", "bob" ]
        }
    }
}'

See REST API for full JSON specification

new ClientAndServer(1080)
    .when(request().withPath("/greeting"))
    .respond(
        template(
            HttpTemplate.TemplateType.VELOCITY,
            "{ 'statusCode': 200, 'headers': { 'X-Served-By': [ '$hostname' ] }, 'body': '$parameters.greeting $lists.pickRandom($parameters.names), pages $lists.arrayJoin(',', $lists.range(1, 3))' }"
        )
            .withParameter("greeting", "hello")
            .withParameter("names", Arrays.asList("ann", "bob"))
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "path": "/greeting"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'X-Served-By': [ '$hostname' ] }, 'body': '$parameters.greeting $lists.pickRandom($parameters.names), pages $lists.arrayJoin(',', $lists.range(1, 3))' }",
        "templateType": "VELOCITY",
        "parameters": {
            "greeting": "hello",
            "names": [ "ann", "bob" ]
        }
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

import requests

requests.put("http://localhost:1080/mockserver/expectation", json={
    "httpRequest": {
        "path": "/greeting"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'X-Served-By': [ '$hostname' ] }, 'body': '$parameters.greeting $lists.pickRandom($parameters.names), pages $lists.arrayJoin(',', $lists.range(1, 3))' }",
        "templateType": "VELOCITY",
        "parameters": {
            "greeting": "hello",
            "names": [ "ann", "bob" ]
        }
    }
})
require 'net/http'

uri = URI('http://localhost:1080/mockserver/expectation')
Net::HTTP.start(uri.host, uri.port) do |http|
  http.send_request('PUT', uri.path, <<~'JSON', 'Content-Type' => 'application/json')
    {
        "httpRequest": {
            "path": "/greeting"
        },
        "httpResponseTemplate": {
            "template": "{ 'statusCode': 200, 'headers': { 'X-Served-By': [ '$hostname' ] }, 'body': '$parameters.greeting $lists.pickRandom($parameters.names), pages $lists.arrayJoin(',', $lists.range(1, 3))' }",
            "templateType": "VELOCITY",
            "parameters": {
                "greeting": "hello",
                "names": [ "ann", "bob" ]
            }
        }
    }
  JSON
end
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
    "httpRequest": {
        "path": "/greeting"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'X-Served-By': [ '$hostname' ] }, 'body': '$parameters.greeting $lists.pickRandom($parameters.names), pages $lists.arrayJoin(',', $lists.range(1, 3))' }",
        "templateType": "VELOCITY",
        "parameters": {
            "greeting": "hello",
            "names": [ "ann", "bob" ]
        }
    }
}`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""path"": ""/greeting""
    },
    ""httpResponseTemplate"": {
        ""template"": ""{ 'statusCode': 200, 'headers': { 'X-Served-By': [ '$hostname' ] }, 'body': '$parameters.greeting $lists.pickRandom($parameters.names), pages $lists.arrayJoin(',', $lists.range(1, 3))' }"",
        ""templateType"": ""VELOCITY"",
        ""parameters"": {
            ""greeting"": ""hello"",
            ""names"": [ ""ann"", ""bob"" ]
        }
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "path": "/greeting"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'X-Served-By': [ '$hostname' ] }, 'body': '$parameters.greeting $lists.pickRandom($parameters.names), pages $lists.arrayJoin(',', $lists.range(1, 3))' }",
        "templateType": "VELOCITY",
        "parameters": {
            "greeting": "hello",
            "names": [ "ann", "bob" ]
        }
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "path": "/greeting"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'X-Served-By': [ '$hostname' ] }, 'body': '$parameters.greeting $lists.pickRandom($parameters.names), pages $lists.arrayJoin(',', $lists.range(1, 3))' }",
        "templateType": "VELOCITY",
        "parameters": {
            "greeting": "hello",
            "names": [ "ann", "bob" ]
        }
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "path": "/greeting"
    },
    "httpResponseTemplate": {
        "template": "{ '\''statusCode'\'': 200, '\''headers'\'': { '\''X-Served-By'\'': [ '\''$hostname'\'' ] }, '\''body'\'': '\''$parameters.greeting $lists.pickRandom($parameters.names), pages $lists.arrayJoin('\'','\'', $lists.range(1, 3))'\'' }",
        "templateType": "VELOCITY",
        "parameters": {
            "greeting": "hello",
            "names": [ "ann", "bob" ]
        }
    }
}'

See REST API for full JSON specification

new ClientAndServer(1080)
    .when(request().withPath("/greeting"))
    .respond(
        template(
            HttpTemplate.TemplateType.JAVASCRIPT,
            "return { 'statusCode': 200, 'headers': { 'X-Served-By': [ hostname ] }, 'body': parameters.greeting + ' ' + lists.pickRandom(parameters.names) + ', pages ' + lists.arrayJoin(',', lists.range(1, 3)) };"
        )
            .withParameter("greeting", "hello")
            .withParameter("names", Arrays.asList("ann", "bob"))
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "path": "/greeting"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 200, 'headers': { 'X-Served-By': [ hostname ] }, 'body': parameters.greeting + ' ' + lists.pickRandom(parameters.names) + ', pages ' + lists.arrayJoin(',', lists.range(1, 3)) };",
        "templateType": "JAVASCRIPT",
        "parameters": {
            "greeting": "hello",
            "names": [ "ann", "bob" ]
        }
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

import requests

requests.put("http://localhost:1080/mockserver/expectation", json={
    "httpRequest": {
        "path": "/greeting"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 200, 'headers': { 'X-Served-By': [ hostname ] }, 'body': parameters.greeting + ' ' + lists.pickRandom(parameters.names) + ', pages ' + lists.arrayJoin(',', lists.range(1, 3)) };",
        "templateType": "JAVASCRIPT",
        "parameters": {
            "greeting": "hello",
            "names": [ "ann", "bob" ]
        }
    }
})
require 'net/http'

uri = URI('http://localhost:1080/mockserver/expectation')
Net::HTTP.start(uri.host, uri.port) do |http|
  http.send_request('PUT', uri.path, <<~'JSON', 'Content-Type' => 'application/json')
    {
        "httpRequest": {
            "path": "/greeting"
        },
        "httpResponseTemplate": {
            "template": "return { 'statusCode': 200, 'headers': { 'X-Served-By': [ hostname ] }, 'body': parameters.greeting + ' ' + lists.pickRandom(parameters.names) + ', pages ' + lists.arrayJoin(',', lists.range(1, 3)) };",
            "templateType": "JAVASCRIPT",
            "parameters": {
                "greeting": "hello",
                "names": [ "ann", "bob" ]
            }
        }
    }
  JSON
end
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
    "httpRequest": {
        "path": "/greeting"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 200, 'headers': { 'X-Served-By': [ hostname ] }, 'body': parameters.greeting + ' ' + lists.pickRandom(parameters.names) + ', pages ' + lists.arrayJoin(',', lists.range(1, 3)) };",
        "templateType": "JAVASCRIPT",
        "parameters": {
            "greeting": "hello",
            "names": [ "ann", "bob" ]
        }
    }
}`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""path"": ""/greeting""
    },
    ""httpResponseTemplate"": {
        ""template"": ""return { 'statusCode': 200, 'headers': { 'X-Served-By': [ hostname ] }, 'body': parameters.greeting + ' ' + lists.pickRandom(parameters.names) + ', pages ' + lists.arrayJoin(',', lists.range(1, 3)) };"",
        ""templateType"": ""JAVASCRIPT"",
        ""parameters"": {
            ""greeting"": ""hello"",
            ""names"": [ ""ann"", ""bob"" ]
        }
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "path": "/greeting"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 200, 'headers': { 'X-Served-By': [ hostname ] }, 'body': parameters.greeting + ' ' + lists.pickRandom(parameters.names) + ', pages ' + lists.arrayJoin(',', lists.range(1, 3)) };",
        "templateType": "JAVASCRIPT",
        "parameters": {
            "greeting": "hello",
            "names": [ "ann", "bob" ]
        }
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "path": "/greeting"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 200, 'headers': { 'X-Served-By': [ hostname ] }, 'body': parameters.greeting + ' ' + lists.pickRandom(parameters.names) + ', pages ' + lists.arrayJoin(',', lists.range(1, 3)) };",
        "templateType": "JAVASCRIPT",
        "parameters": {
            "greeting": "hello",
            "names": [ "ann", "bob" ]
        }
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "path": "/greeting"
    },
    "httpResponseTemplate": {
        "template": "return { '\''statusCode'\'': 200, '\''headers'\'': { '\''X-Served-By'\'': [ hostname ] }, '\''body'\'': parameters.greeting + '\'' '\'' + lists.pickRandom(parameters.names) + '\'', pages '\'' + lists.arrayJoin('\'','\'', lists.range(1, 3)) };",
        "templateType": "JAVASCRIPT",
        "parameters": {
            "greeting": "hello",
            "names": [ "ann", "bob" ]
        }
    }
}'

See REST API for full JSON specification

 

Template Helper Objects

In addition to the simple dynamic variables above, all template formats have access to helper objects that provide rich functionality for string manipulation, JSON processing, date/time arithmetic, math operations, JWT generation, and realistic fake data generation. These helpers are accessed as objects with methods.

 

JWT Helper (jwt)

Generates signed JSON Web Tokens (JWTs) and JSON Web Key Sets (JWKS) for testing OAuth2/OIDC flows. Tokens are signed with RS256 (RSA with SHA-256) using an auto-generated key pair.

MethodDescriptionExample Output
jwt.generate()Generate a JWT with default claims (sub, iss, aud, iat, exp)eyJhbGciOiJSUzI1NiJ9...
jwt.generate(claims)Generate a JWT with custom claims (Velocity/JS only)eyJhbGciOiJSUzI1NiJ9...
jwt.jwks()Return the JWKS (public key set) for verifying tokens{"keys":[{"kty":"RSA",...}]}

Usage examples:

  • Mustache: {{ jwt }} (generates a default JWT) or {{ jwt.jwks }}
  • Velocity: $jwt.generate() or $jwt.jwks()
  • JavaScript: jwt.generate() or jwt.jwks()
 

String Helper (strings)

Provides string manipulation functions useful for transforming request data in response templates.

MethodDescriptionExample
strings.trim(value)Remove leading and trailing whitespacestrings.trim(" hello ") → "hello"
strings.capitalize(value)Capitalize the first letterstrings.capitalize("hello") → "Hello"
strings.uppercase(value)Convert to uppercasestrings.uppercase("hello") → "HELLO"
strings.lowercase(value)Convert to lowercasestrings.lowercase("HELLO") → "hello"
strings.urlEncode(value)URL-encode a stringstrings.urlEncode("a=b") → "a%3Db"
strings.urlDecode(value)URL-decode a stringstrings.urlDecode("a%3Db") → "a=b"
strings.base64Encode(value)Base64-encode a stringstrings.base64Encode("hello") → "aGVsbG8="
strings.base64Decode(value)Base64-decode a stringstrings.base64Decode("aGVsbG8=") → "hello"
strings.substringBefore(value, sep)Get substring before the first occurrence of separatorstrings.substringBefore("a-b", "-") → "a"
strings.substringAfter(value, sep)Get substring after the first occurrence of separatorstrings.substringAfter("a-b", "-") → "b"
strings.length(value)Return the length of a stringstrings.length("hello") → 5
strings.contains(value, search)Check if a string contains a substringstrings.contains("hello", "ell") → true
strings.replace(value, target, replacement)Replace all occurrences of target with replacementstrings.replace("hello", "l", "r") → "herro"

Usage examples:

  • Velocity: $strings.uppercase($!request.method)
  • JavaScript: strings.base64Encode(request.body)
 

JSON Transform Helper (jsonTransform)

Provides JSON manipulation functions for merging, sorting, and extracting data from JSON strings. Named jsonTransform to avoid collision with Velocity's built-in $json (JsonTool).

MethodDescriptionExample
jsonTransform.merge(json1, json2)Merge two JSON objects (json2 fields overwrite json1)jsonTransform.merge('{"a":1}', '{"b":2}') → '{"a":1,"b":2}'
jsonTransform.sort(jsonArray, field)Sort a JSON array of objects by a fieldjsonTransform.sort('[{"n":"b"},{"n":"a"}]', "n")
jsonTransform.arrayAdd(jsonArray, element)Add an element to a JSON arrayjsonTransform.arrayAdd('[1,2]', '3') → '[1,2,3]'
jsonTransform.remove(json, fieldName)Remove a field from a JSON objectjsonTransform.remove('{"a":1,"b":2}', "a") → '{"b":2}'
jsonTransform.prettyPrint(json)Pretty-print a JSON stringjsonTransform.prettyPrint('{"a":1}')
jsonTransform.field(json, fieldName)Extract a single field value from a JSON objectjsonTransform.field('{"name":"test"}', "name") → "test"
jsonTransform.size(jsonArray)Return the number of elements in a JSON arrayjsonTransform.size('[1,2,3]') → 3

Usage examples:

  • Velocity: $jsonTransform.field($!request.body, "username")
  • JavaScript: jsonTransform.merge(request.body, '{"status":"processed"}')
 

Date Helper (dates)

Reads, offsets, truncates and formats dates. The helper can work on the current time or on any date, such as one taken from the request, in any time zone. The current time comes from the server clock, so freezing or advancing the clock changes it too. The methods that take no date work on the current time in UTC and return text, as they always have:

MethodDescriptionExample Output
dates.format(pattern)Format current time with a pattern (e.g., yyyy-MM-dd), or iso, rfc1123, epochSeconds or epochMillis"2026-05-11"
dates.plusSeconds(n)Current time plus n seconds (ISO-8601)"2026-05-11T10:15:30Z"
dates.plusMinutes(n)Current time plus n minutes (ISO-8601)"2026-05-11T10:20:00Z"
dates.plusHours(n)Current time plus n hours (ISO-8601)"2026-05-11T11:15:00Z"
dates.plusDays(n)Current time plus n days (ISO-8601)"2026-05-12T10:15:00Z"
dates.minusSeconds(n)Current time minus n seconds (ISO-8601)
dates.minusMinutes(n)Current time minus n minutes (ISO-8601)
dates.minusHours(n)Current time minus n hours (ISO-8601)
dates.minusDays(n)Current time minus n days (ISO-8601)
dates.epochSeconds()Current time as Unix epoch seconds1747048530
dates.epochMillis()Current time as Unix epoch milliseconds1747048530000
dates.epochSecondsPlus(n)Epoch seconds plus n seconds"1747048590"
dates.epochSecondsMinus(n)Epoch seconds minus n seconds"1747048470"
dates.rfc1123()Current time in the HTTP date format, for headers such as Expires and Last-Modified"Wed, 21 Oct 2026 15:04:05 GMT"

The following methods take a date or return one. A date can be a date returned by another dates method, text, or a number:

  • ISO-8601 text: 2026-10-21T15:04:05Z, 2026-10-21T16:04:05+01:00, 2026-10-21T15:04:05 or 2026-10-21. Text without an offset is read as UTC.
  • RFC-1123 text, the format HTTP headers use: Wed, 21 Oct 2026 15:04:05 GMT.
  • Epoch seconds or epoch milliseconds, as a number or digits. A value below 100,000,000,000 is read as seconds and anything larger as milliseconds. To choose explicitly, pass epochSeconds or epochMillis as the pattern.
  • now, the current time. An empty value, such as a header the request does not have, is not a date and makes the template fail.

When text is not in one of these formats, pass the pattern it is written in. Text read with a pattern must be a real date: 2026-02-30 fails rather than becoming 28 February, and a 12-hour hh needs an AM/PM marker (a). A time zone is a region such as Europe/London, an offset such as +01:00, or UTC. Prefer a region to a three-letter abbreviation: EST is a fixed -05:00 all year, while PST means America/Los_Angeles. A local time that falls in a daylight-saving gap moves forward by the length of the gap (01:30 on the day London's clocks go forward becomes 02:30), and one that occurs twice takes the earlier offset. Month and day names are in English. A returned date prints as ISO-8601: in UTC as 2026-10-21T15:04:05Z, and in other time zones with the offset, as in 2026-10-21T16:04:05+01:00.

MethodDescriptionExample
dates.now()The current time in UTC, as a datedates.now().plusHours(1)
dates.now(zone)The current time in a time zonedates.now("Europe/London").format("HH:mm") → "16:04"
dates.parse(date)Read a date in any of the formats abovedates.parse("Wed, 21 Oct 2026 15:04:05 GMT") → 2026-10-21T15:04:05Z
dates.parse(text, pattern)Read text written in a pattern, read as UTC if the pattern has no zone or offsetdates.parse("21/10/2026", "dd/MM/yyyy") → 2026-10-21T00:00:00Z
dates.parse(text, pattern, zone)Read text in a time zone, giving a date in that zonedates.parse("21/10/2026 16:04", "dd/MM/yyyy HH:mm", "Europe/London") → 2026-10-21T16:04:00+01:00
dates.format(date, pattern)Format a date in its own time zone. The pattern can also be iso, rfc1123, epochSeconds or epochMillisdates.format("2026-10-21T15:04:05Z", "yyyy-MM-dd") → "2026-10-21"
dates.format(date, pattern, zone)Format a date in a time zonedates.format("2026-10-21T15:04:05Z", "HH:mm", "America/New_York") → "11:04"
dates.offset(date, offset)Move a date by an amount and a unit (seconds, minutes, hours, days, weeks, months or years), or by an ISO-8601 period such as P1D, -P1M or PT90Mdates.offset("2026-10-21T15:04:05Z", "-2 hours") → 2026-10-21T13:04:05Z
dates.truncate(date, truncation)Move a date back to the start of a period, in the date's time zone (see the truncations below)dates.truncate("2026-10-21T15:04:05Z", "firstDayOfNextMonth") → 2026-11-01T00:00:00Z
dates.withZone(date, zone)Show the same moment in another time zonedates.withZone("2026-10-21T15:04:05Z", "Asia/Tokyo") → 2026-10-22T00:04:05+09:00
dates.rfc1123(date)Format a date in the HTTP date format, always in GMT and with a two-digit daydates.rfc1123("2026-10-01T09:00:00Z") → "Thu, 01 Oct 2026 09:00:00 GMT"
dates.epochSeconds(date), dates.epochMillis(date)A date as Unix epoch seconds or millisecondsdates.epochSeconds("2026-10-21T15:04:05Z") → 1792595045

Each returned date has its own methods, so calls can be chained, for example dates.parse(text).plusDays(1).startOfDay().rfc1123():

  • Offsets: plusSeconds(n), plusMinutes(n), plusHours(n), plusDays(n), plusWeeks(n), plusMonths(n), plusYears(n), the matching minus methods, and offset("1 days"). A month or year offset that lands past the end of a month gives that month's last day, so 31 January plus one month is 28 February.
  • Truncations: startOfSecond(), startOfMinute(), startOfHour(), startOfDay(), firstDayOfMonth(), lastDayOfMonth(), firstDayOfNextMonth(), firstDayOfYear(), lastDayOfYear(), firstDayOfNextYear(), and truncate(name). Every truncation to a day gives the start of that day (00:00). truncate also accepts the names with spaces, such as "first day of month", and WireMock's "first minute of hour" and "first hour of day".
  • Output: format(pattern), iso(), rfc1123(), epochSeconds(), epochMillis(), withZone(zone), and isBefore(date) and isAfter(date) for conditions.

Mustache cannot pass arguments to a method. Instead, it can use the methods that take no arguments, for example {{ dates.rfc1123 }} or {{ dates.now.firstDayOfNextMonth.rfc1123 }}, and the dates.render section. The section's content is a date followed by options separated by |: {{#dates.render}}{{ request.headers.Date.0 }}|offset=1 hours|format=rfc1123{{/dates.render}}.

OptionDescription
(first part)The date, in any of the formats above, or now for the current time. An empty date, for example from a missing header, makes the template fail
pattern=The pattern the date is written in
zone=Show the date in this time zone. The first zone= is also used to read a date that has no zone or offset
offset=Move the date, such as offset=-1 days or offset=P1M
truncate=Truncate the date, such as truncate=firstDayOfMonth
format=How to write the result (a pattern, iso, rfc1123, epochSeconds or epochMillis). It must come last. Without it, the result is ISO-8601

The zone=, offset= and truncate= options apply in the order they are written, so truncate=firstDayOfMonth|offset=-1 days gives the last day of the previous month. A pattern in format= cannot contain |. A value rendered into the section from the request, such as a header, cannot add options either: if it contains | the template fails.

Usage examples:

  • Velocity: $dates.plusHours(1) (token expiry 1 hour from now), $dates.rfc1123($dates.offset($!request.headers['Date'][0], '1 hours')) (one hour after the request's Date header)
  • JavaScript: dates.format("yyyy-MM-dd"), dates.now("Europe/London").lastDayOfMonth().format("d MMMM yyyy")
  • Mustache: {{ dates }} (outputs current ISO-8601 timestamp via toString), {{#dates.render}}now|zone=Europe/London|truncate=startOfDay{{/dates.render}}

An invalid date, pattern, time zone, offset or truncation makes the template fail, with an error that names the value and lists what is accepted.

The examples below return an Expires header one hour from now and a Last-Modified header for the start of today, both in the HTTP date format, and a body with the last day of the month in London, such as billing period ends 31 October 2026:

new ClientAndServer(1080)
    .when(request().withPath("/subscription"))
    .respond(
        template(
            HttpTemplate.TemplateType.MUSTACHE,
            "{ 'statusCode': 200, 'headers': { 'Expires': [ '{{#dates.render}}now|offset=1 hours|format=rfc1123{{/dates.render}}' ], 'Last-Modified': [ '{{ dates.now.startOfDay.rfc1123 }}' ] }, 'body': 'billing period ends {{#dates.render}}now|zone=Europe/London|truncate=lastDayOfMonth|format=d MMMM yyyy{{/dates.render}}' }"
        )
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "path": "/subscription"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'Expires': [ '{{#dates.render}}now|offset=1 hours|format=rfc1123{{/dates.render}}' ], 'Last-Modified': [ '{{ dates.now.startOfDay.rfc1123 }}' ] }, 'body': 'billing period ends {{#dates.render}}now|zone=Europe/London|truncate=lastDayOfMonth|format=d MMMM yyyy{{/dates.render}}' }",
        "templateType": "MUSTACHE"
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "path": "/subscription"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'Expires': [ '{{#dates.render}}now|offset=1 hours|format=rfc1123{{/dates.render}}' ], 'Last-Modified': [ '{{ dates.now.startOfDay.rfc1123 }}' ] }, 'body': 'billing period ends {{#dates.render}}now|zone=Europe/London|truncate=lastDayOfMonth|format=d MMMM yyyy{{/dates.render}}' }",
        "templateType": "MUSTACHE"
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    'httpRequest' => {
        'path' => '/subscription'
    },
    'httpResponseTemplate' => {
        'template' => "{ 'statusCode': 200, 'headers': { 'Expires': [ '{{#dates.render}}now|offset=1 hours|format=rfc1123{{/dates.render}}' ], 'Last-Modified': [ '{{ dates.now.startOfDay.rfc1123 }}' ] }, 'body': 'billing period ends {{#dates.render}}now|zone=Europe/London|truncate=lastDayOfMonth|format=d MMMM yyyy{{/dates.render}}' }",
        'templateType' => 'MUSTACHE'
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
    "httpRequest": {
        "path": "/subscription"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'Expires': [ '{{#dates.render}}now|offset=1 hours|format=rfc1123{{/dates.render}}' ], 'Last-Modified': [ '{{ dates.now.startOfDay.rfc1123 }}' ] }, 'body': 'billing period ends {{#dates.render}}now|zone=Europe/London|truncate=lastDayOfMonth|format=d MMMM yyyy{{/dates.render}}' }",
        "templateType": "MUSTACHE"
    }
}`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""path"": ""/subscription""
    },
    ""httpResponseTemplate"": {
        ""template"": ""{ 'statusCode': 200, 'headers': { 'Expires': [ '{{#dates.render}}now|offset=1 hours|format=rfc1123{{/dates.render}}' ], 'Last-Modified': [ '{{ dates.now.startOfDay.rfc1123 }}' ] }, 'body': 'billing period ends {{#dates.render}}now|zone=Europe/London|truncate=lastDayOfMonth|format=d MMMM yyyy{{/dates.render}}' }"",
        ""templateType"": ""MUSTACHE""
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "path": "/subscription"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'Expires': [ '{{#dates.render}}now|offset=1 hours|format=rfc1123{{/dates.render}}' ], 'Last-Modified': [ '{{ dates.now.startOfDay.rfc1123 }}' ] }, 'body': 'billing period ends {{#dates.render}}now|zone=Europe/London|truncate=lastDayOfMonth|format=d MMMM yyyy{{/dates.render}}' }",
        "templateType": "MUSTACHE"
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "path": "/subscription"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'Expires': [ '{{#dates.render}}now|offset=1 hours|format=rfc1123{{/dates.render}}' ], 'Last-Modified': [ '{{ dates.now.startOfDay.rfc1123 }}' ] }, 'body': 'billing period ends {{#dates.render}}now|zone=Europe/London|truncate=lastDayOfMonth|format=d MMMM yyyy{{/dates.render}}' }",
        "templateType": "MUSTACHE"
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "path": "/subscription"
    },
    "httpResponseTemplate": {
        "template": "{ '\''statusCode'\'': 200, '\''headers'\'': { '\''Expires'\'': [ '\''{{#dates.render}}now|offset=1 hours|format=rfc1123{{/dates.render}}'\'' ], '\''Last-Modified'\'': [ '\''{{ dates.now.startOfDay.rfc1123 }}'\'' ] }, '\''body'\'': '\''billing period ends {{#dates.render}}now|zone=Europe/London|truncate=lastDayOfMonth|format=d MMMM yyyy{{/dates.render}}'\'' }",
        "templateType": "MUSTACHE"
    }
}'

See REST API for full JSON specification

new ClientAndServer(1080)
    .when(request().withPath("/subscription"))
    .respond(
        template(
            HttpTemplate.TemplateType.VELOCITY,
            "{ 'statusCode': 200, 'headers': { 'Expires': [ '$dates.now().plusHours(1).rfc1123()' ], 'Last-Modified': [ '$dates.now().startOfDay().rfc1123()' ] }, 'body': 'billing period ends $dates.now('Europe/London').lastDayOfMonth().format('d MMMM yyyy')' }"
        )
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "path": "/subscription"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'Expires': [ '$dates.now().plusHours(1).rfc1123()' ], 'Last-Modified': [ '$dates.now().startOfDay().rfc1123()' ] }, 'body': 'billing period ends $dates.now('Europe/London').lastDayOfMonth().format('d MMMM yyyy')' }",
        "templateType": "VELOCITY"
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "path": "/subscription"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'Expires': [ '$dates.now().plusHours(1).rfc1123()' ], 'Last-Modified': [ '$dates.now().startOfDay().rfc1123()' ] }, 'body': 'billing period ends $dates.now('Europe/London').lastDayOfMonth().format('d MMMM yyyy')' }",
        "templateType": "VELOCITY"
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    'httpRequest' => {
        'path' => '/subscription'
    },
    'httpResponseTemplate' => {
        'template' => "{ 'statusCode': 200, 'headers': { 'Expires': [ '$dates.now().plusHours(1).rfc1123()' ], 'Last-Modified': [ '$dates.now().startOfDay().rfc1123()' ] }, 'body': 'billing period ends $dates.now('Europe/London').lastDayOfMonth().format('d MMMM yyyy')' }",
        'templateType' => 'VELOCITY'
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
    "httpRequest": {
        "path": "/subscription"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'Expires': [ '$dates.now().plusHours(1).rfc1123()' ], 'Last-Modified': [ '$dates.now().startOfDay().rfc1123()' ] }, 'body': 'billing period ends $dates.now('Europe/London').lastDayOfMonth().format('d MMMM yyyy')' }",
        "templateType": "VELOCITY"
    }
}`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""path"": ""/subscription""
    },
    ""httpResponseTemplate"": {
        ""template"": ""{ 'statusCode': 200, 'headers': { 'Expires': [ '$dates.now().plusHours(1).rfc1123()' ], 'Last-Modified': [ '$dates.now().startOfDay().rfc1123()' ] }, 'body': 'billing period ends $dates.now('Europe/London').lastDayOfMonth().format('d MMMM yyyy')' }"",
        ""templateType"": ""VELOCITY""
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "path": "/subscription"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'Expires': [ '$dates.now().plusHours(1).rfc1123()' ], 'Last-Modified': [ '$dates.now().startOfDay().rfc1123()' ] }, 'body': 'billing period ends $dates.now('Europe/London').lastDayOfMonth().format('d MMMM yyyy')' }",
        "templateType": "VELOCITY"
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "path": "/subscription"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'Expires': [ '$dates.now().plusHours(1).rfc1123()' ], 'Last-Modified': [ '$dates.now().startOfDay().rfc1123()' ] }, 'body': 'billing period ends $dates.now('Europe/London').lastDayOfMonth().format('d MMMM yyyy')' }",
        "templateType": "VELOCITY"
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "path": "/subscription"
    },
    "httpResponseTemplate": {
        "template": "{ '\''statusCode'\'': 200, '\''headers'\'': { '\''Expires'\'': [ '\''$dates.now().plusHours(1).rfc1123()'\'' ], '\''Last-Modified'\'': [ '\''$dates.now().startOfDay().rfc1123()'\'' ] }, '\''body'\'': '\''billing period ends $dates.now('\''Europe/London'\'').lastDayOfMonth().format('\''d MMMM yyyy'\'')'\'' }",
        "templateType": "VELOCITY"
    }
}'

See REST API for full JSON specification

new ClientAndServer(1080)
    .when(request().withPath("/subscription"))
    .respond(
        template(
            HttpTemplate.TemplateType.JAVASCRIPT,
            "return { 'statusCode': 200, 'headers': { 'Expires': [ dates.now().plusHours(1).rfc1123() ], 'Last-Modified': [ dates.now().startOfDay().rfc1123() ] }, 'body': 'billing period ends ' + dates.now('Europe/London').lastDayOfMonth().format('d MMMM yyyy') };"
        )
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "path": "/subscription"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 200, 'headers': { 'Expires': [ dates.now().plusHours(1).rfc1123() ], 'Last-Modified': [ dates.now().startOfDay().rfc1123() ] }, 'body': 'billing period ends ' + dates.now('Europe/London').lastDayOfMonth().format('d MMMM yyyy') };",
        "templateType": "JAVASCRIPT"
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "path": "/subscription"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 200, 'headers': { 'Expires': [ dates.now().plusHours(1).rfc1123() ], 'Last-Modified': [ dates.now().startOfDay().rfc1123() ] }, 'body': 'billing period ends ' + dates.now('Europe/London').lastDayOfMonth().format('d MMMM yyyy') };",
        "templateType": "JAVASCRIPT"
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    'httpRequest' => {
        'path' => '/subscription'
    },
    'httpResponseTemplate' => {
        'template' => "return { 'statusCode': 200, 'headers': { 'Expires': [ dates.now().plusHours(1).rfc1123() ], 'Last-Modified': [ dates.now().startOfDay().rfc1123() ] }, 'body': 'billing period ends ' + dates.now('Europe/London').lastDayOfMonth().format('d MMMM yyyy') };",
        'templateType' => 'JAVASCRIPT'
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
    "httpRequest": {
        "path": "/subscription"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 200, 'headers': { 'Expires': [ dates.now().plusHours(1).rfc1123() ], 'Last-Modified': [ dates.now().startOfDay().rfc1123() ] }, 'body': 'billing period ends ' + dates.now('Europe/London').lastDayOfMonth().format('d MMMM yyyy') };",
        "templateType": "JAVASCRIPT"
    }
}`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""path"": ""/subscription""
    },
    ""httpResponseTemplate"": {
        ""template"": ""return { 'statusCode': 200, 'headers': { 'Expires': [ dates.now().plusHours(1).rfc1123() ], 'Last-Modified': [ dates.now().startOfDay().rfc1123() ] }, 'body': 'billing period ends ' + dates.now('Europe/London').lastDayOfMonth().format('d MMMM yyyy') };"",
        ""templateType"": ""JAVASCRIPT""
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "path": "/subscription"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 200, 'headers': { 'Expires': [ dates.now().plusHours(1).rfc1123() ], 'Last-Modified': [ dates.now().startOfDay().rfc1123() ] }, 'body': 'billing period ends ' + dates.now('Europe/London').lastDayOfMonth().format('d MMMM yyyy') };",
        "templateType": "JAVASCRIPT"
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "path": "/subscription"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 200, 'headers': { 'Expires': [ dates.now().plusHours(1).rfc1123() ], 'Last-Modified': [ dates.now().startOfDay().rfc1123() ] }, 'body': 'billing period ends ' + dates.now('Europe/London').lastDayOfMonth().format('d MMMM yyyy') };",
        "templateType": "JAVASCRIPT"
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "path": "/subscription"
    },
    "httpResponseTemplate": {
        "template": "return { '\''statusCode'\'': 200, '\''headers'\'': { '\''Expires'\'': [ dates.now().plusHours(1).rfc1123() ], '\''Last-Modified'\'': [ dates.now().startOfDay().rfc1123() ] }, '\''body'\'': '\''billing period ends '\'' + dates.now('\''Europe/London'\'').lastDayOfMonth().format('\''d MMMM yyyy'\'') };",
        "templateType": "JAVASCRIPT"
    }
}'

See REST API for full JSON specification

 

Calc Helper (calc)

Provides mathematical functions for generating random numbers, rounding, and formatting. Named calc to avoid collision with Velocity's built-in $math (MathTool).

MethodDescriptionExample
calc.randomInt(min, max)Random integer between min and max (inclusive)calc.randomInt(1, 100) → 42
calc.randomDouble()Random double between 0.0 and 1.00.7312...
calc.randomDouble(min, max)Random double between min and maxcalc.randomDouble(1.0, 5.0)
calc.abs(value)Absolute valuecalc.abs(-5) → 5
calc.min(a, b)Minimum of two integerscalc.min(3, 7) → 3
calc.max(a, b)Maximum of two integerscalc.max(3, 7) → 7
calc.round(value, scale)Round to specified decimal placescalc.round(3.14159, 2) → 3.14
calc.format(value, pattern)Format a number with a DecimalFormat patterncalc.format(1234.5, "#,##0.00") → "1,234.50"
calc.ceil(value)Round up to nearest integercalc.ceil(3.1) → 4.0
calc.floor(value)Round down to nearest integercalc.floor(3.9) → 3.0

Usage examples:

  • Velocity: $calc.randomInt(1, 1000)
  • JavaScript: calc.round(calc.randomDouble(0.0, 100.0), 2)
 

Faker Helper (faker)

Generates realistic fake data for names, addresses, emails, phone numbers, and 250+ other categories using DataFaker. A single shared Faker instance is exposed as faker in all template engines. The instance is thread-safe and produces random values on each invocation.

ExpressionDescriptionExample Output
faker.name().firstName()Random first name"Emory"
faker.name().lastName()Random last name"Johnson"
faker.name().fullName()Random full name"Dr. Jane Smith"
faker.internet().emailAddress()Random email address"john.doe@example.com"
faker.internet().url()Random URL"https://www.example.com"
faker.address().city()Random city name"Brittneymouth"
faker.address().streetAddress()Random street address"123 Main St"
faker.address().zipCode()Random ZIP/postal code"90210"
faker.phoneNumber().cellPhone()Random phone number"(555) 123-4567"
faker.lorem().sentence()Random sentence of text"Lorem ipsum dolor sit amet."
faker.number().numberBetween(1, 100)Random integer in range42
faker.company().name()Random company name"Acme Corp"

Usage examples:

  • Velocity: $faker.name().firstName() or $faker.internet().emailAddress()
  • Mustache: {{ faker.name.firstName }} or {{ faker.internet.emailAddress }}
  • JavaScript: faker.name().firstName() or faker.internet().emailAddress()

The full list of available providers (name, address, internet, company, finance, medical, etc.) is documented in the DataFaker providers reference.

Data is in English by default. For data from another country or language, such as German names or French cities, use a locale. A locale can be a language, such as de, or a language and country, such as de-DE, fr_CA or pt-BR; anything after the country, such as a script or extension, is ignored. Only the locales DataFaker has data for are used: a country it has no data for uses the language (de-BE gives de), and a language it has no data for gives English data and logs a warning. So a locale taken from the request, such as the Accept-Language header, is safe to use. When templateFakerSeed is set, each locale gives the same values on every run.

EngineExample
Velocity$faker.locale('de-DE').name().fullName(), or #set($german = $faker.locale('de')) once and then $german.address().city()
JavaScriptfaker.locale('fr').address().city()
Mustache{{ faker.locale.de-DE.name.fullName }}, or for several values {{#faker.locale.fr}}{{ name.fullName }}, {{ address.city }}{{/faker.locale.fr}}
 

Crypto Helper (crypto)

Computes cryptographic digests and HMAC signatures. All methods return lowercase hex strings. Use this to generate request signatures, verify webhook payloads, or add content-hashing headers in templated responses.

MethodDescriptionExample
crypto.md5(s)MD5 digest of a stringcrypto.md5("hello") → "5d41402abc4b2a76b9719d911017c592"
crypto.sha1(s)SHA-1 digestcrypto.sha1("hello") → "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d"
crypto.sha256(s)SHA-256 digestcrypto.sha256("hello") → "2cf24dba5fb0a30e26e83b2ac5b9e29e..."
crypto.sha512(s)SHA-512 digestcrypto.sha512("hello") → "9b71d224bd62f3785d96d46ad3ea3d..."
crypto.hmacSha256(key, data)HMAC-SHA-256 of data using keycrypto.hmacSha256("secret", "payload") → lowercase hex

Usage examples:

  • Velocity: $crypto.sha256($!request.body) (hash the request body into a response field)
  • Mustache: {{ crypto.sha256 }} is not directly callable with arguments in Mustache — use Velocity or JavaScript when you need to pass a value to a crypto method
  • JavaScript: crypto.hmacSha256("my-secret", request.body)
 

Regex Helper (regex)

Applies Java regular expressions to strings inside a template. Use this to extract captured groups from request data or rewrite parts of a value before including it in the response.

MethodDescriptionExample
regex.matches(input, pattern)Returns true if the pattern matches anywhere in the inputregex.matches("hello world", "\\bworld\\b") → true
regex.replaceAll(input, pattern, replacement)Replaces all matches of the pattern with the replacement string; supports $1 back-referencesregex.replaceAll("foo123bar", "[0-9]+", "NUM") → "fooNUMbar"
regex.group(input, pattern, groupIndex)Returns the captured group at groupIndex (1-based) from the first match, or "" if there is no matchregex.group("user-42", "user-([0-9]+)", 1) → "42"

Usage examples:

  • Velocity: $regex.group($!request.path, "/orders/([^/]+)", 1) (extract the order ID from the path)
  • JavaScript: regex.replaceAll(request.headers["Authorization"][0], "^Bearer ", "") (strip the Bearer prefix from a token)
  • Mustache: regex helper methods require arguments and cannot be invoked from Mustache — use Velocity or JavaScript instead
 

HTML Helper (html)

Escapes and unescapes HTML entities (HTML 4.0). Use this to safely embed request data in an HTML response body, or to recover the original text from an escaped value.

MethodDescriptionExample
html.escape(value)Escapes characters using HTML entitieshtml.escape("<a> & <b>") → "&lt;a&gt; &amp; &lt;b&gt;"
html.unescape(value)Reverses HTML entity escapinghtml.unescape("&lt;a&gt;") → "<a>"

Usage examples:

  • Velocity: $html.escape($!request.body) (safely embed the request body in HTML)
  • JavaScript: html.unescape(request.queryStringParameters["q"][0])
 

CSV Helper (csv)

Parses CSV text into rows (each row a list of fields) and formats a list of fields back into a CSV line. Quoting and quote-doubling follow RFC 4180: fields containing a comma, double-quote, or newline are wrapped in double quotes and embedded quotes are doubled.

MethodDescriptionExample
csv.parse(text)Parses CSV text into a list of rows, each a list of fieldscsv.parse("a,b\n1,2") → [["a","b"],["1","2"]]
csv.row(list)Formats a list of fields into a single CSV line, quoting where requiredcsv.row(["a","b,c"]) → "a,\"b,c\""

Usage examples:

  • JavaScript: csv.parse(request.body)[0][1] (read the second field of the first CSV row)
  • JavaScript: csv.row(["id","name","total"]) (build a CSV header line for a response)
 

XPath Helper (xpath)

Evaluates an XPath expression against an XML document and returns the string result. The XML parser disables DOCTYPE declarations and external entity resolution, so it is safe against XXE; an unparseable document or invalid expression yields "".

MethodDescriptionExample
xpath.evaluate(xml, expression)Returns the string result of the XPath expression against the XMLxpath.evaluate("<o><id>42</id></o>", "/o/id") → "42"
xpath.formatXml(xml)Pretty-prints XML with two spaces per level, keeping any XML declaration, comments and CDATA. Text that is not well-formed XML, XML with a DOCTYPE, XML nested more than 256 elements deep, or a result over four times the size of the input (and over 64 KiB) makes the template failxpath.formatXml("<o><id>42</id></o>") → "<o>\n <id>42</id>\n</o>"

Usage examples:

  • Velocity: $xpath.evaluate($!request.body, "/order/id") (extract an element from an XML request body), $xpath.formatXml($!request.body) (return the request body pretty-printed)
  • JavaScript: xpath.evaluate(request.body, "/order/@status") (read an attribute value), xpath.formatXml(request.body)
  • Mustache: {{#xpath.formatXml}}{{{ request.body }}}{{/xpath.formatXml}}. Use triple braces so the body is not HTML-escaped before it is formatted
 

YAML Helper (yaml)

Converts a YAML document to JSON and reads a top-level field out of a YAML document. Scalar fields are returned verbatim; object/array fields are returned as JSON.

MethodDescriptionExample
yaml.toJson(yaml)Converts a YAML document to its JSON representationyaml.toJson("name: Bob") → "{\"name\":\"Bob\"}"
yaml.parse(yaml, field)Returns a top-level field value (scalars verbatim, objects/arrays as JSON)yaml.parse("name: Bob", "name") → "Bob"

Usage examples:

  • Velocity: $yaml.toJson($!request.body) (turn a YAML request body into a JSON response)
  • JavaScript: yaml.parse(request.body, "version")
 

Lists Helper (lists)

Picks a random value, builds and joins lists, and counts through a range of numbers. These match WireMock's pickRandom, array, arrayJoin and range helpers. When templateFakerSeed is set, pickRandom picks the same sequence on every run, as faker does. A list built by this helper, including a range, has at most 10,000 values, so a number taken from a request cannot make a template build a huge list.

MethodDescriptionExample
lists.pickRandom(value, ...)One of the values, chosen at random. Pass a single list to pick from itlists.pickRandom("red", "green") → "green"
lists.array(value, ...)The values as a listlists.array("a", "b") → ["a", "b"]
lists.arrayJoin(separator, value, ...)The values joined by the separator. Pass a single list to join its values. The result can be at most 64 KiB or four times the length of the values, whichever is morelists.arrayJoin(", ", lists.array("a", "b")) → "a, b"
lists.range(from, to)The whole numbers from from to to, both included; it counts down when to is smaller. A bound can be a number or text, such as a query parameterlists.range(1, 3) → [1, 2, 3]

Mustache cannot pass arguments to a method, so it writes the values in a section, separated by |. Spaces around each value are removed, and empty values are left out. A value rendered into the section from the request must not contain |, or it becomes two values:

MustacheDescriptionExample Output
{{#lists.pickRandom}}red|green|blue{{/lists.pickRandom}}One value, chosen at randomgreen
{{#lists.pickRandom}}{{#request.queryStringParameters.colour}}{{.}}|{{/request.queryStringParameters.colour}}{{/lists.pickRandom}}One value of a list, chosen at randomteal
{{#lists.arrayJoin}}, |a|b|c{{/lists.arrayJoin}}The values joined by the separator, which is written first and kept exactly as writtena, b, c
{{#lists.range.1.5}}{{.}}{{^-last}},{{/-last}}{{/lists.range.1.5}}Repeats the section for each number from the first bound to the second, which are part of the name1,2,3,4,5

Usage examples:

  • Velocity: #foreach($page in $lists.range(1, $!request.queryStringParameters['pages'][0]))...#end, $lists.pickRandom('red', 'green', 'blue')
  • JavaScript: lists.pickRandom(['red', 'green', 'blue']), lists.arrayJoin(',', lists.range(1, 5))
  • Mustache: {{#lists.pickRandom}}red|green|blue{{/lists.pickRandom}}
 

System Value Helper (systemValue)

Reads an environment variable or a Java system property of the MockServer process, such as the region or version a test environment runs in. This matches WireMock's systemValue helper.

Templates can only read names you allow. List them in the templateSystemValueAllowList property when MockServer starts, as exact names or prefixes ending in *, for example -Dmockserver.templateSystemValueAllowList="APP_*,app.version". It is empty by default, so until you set it every name returns an empty value. One list covers both environment variables and system properties. A name that is not on the list returns an empty value too, exactly like a name that is not set, and is logged as a warning once. There is no way to list all the names or values, and PUT /mockserver/configuration cannot change the list. MockServer's own settings, names starting with mockserver. or MOCKSERVER_, and the javax.net.ssl key and trust store passwords, can never be read, whatever the list says.

Anyone who can create expectations can read the values of the names on the list, because they can write a template that returns them, and anyone who calls that expectation or reads the log sees them too. Never list passwords, tokens, keys or other secrets.

MethodDescriptionExample
systemValue.env(name)The value of the environment variable, or an empty value when it is not set or not allowedsystemValue.env("APP_REGION") → "eu-west-2"
systemValue.property(name)The value of the Java system property, or an empty value when it is not set or not allowedsystemValue.property("app.version") → "1.4.2"

Mustache cannot pass arguments to a method, so it writes the name in a section:

MustacheDescriptionExample Output
{{#systemValue.env}}APP_REGION{{/systemValue.env}}The value of the environment variableeu-west-2
{{#systemValue.property}}app.version{{/systemValue.property}}The value of the Java system property1.4.2

The examples below assume MockServer was started with -Dmockserver.templateSystemValueAllowList="APP_REGION,app.version", and return an X-Region header with the APP_REGION environment variable and a body with the app.version system property, such as version 1.4.2:

new ClientAndServer(1080)
    .when(request().withPath("/region"))
    .respond(
        template(
            HttpTemplate.TemplateType.MUSTACHE,
            "{ 'statusCode': 200, 'headers': { 'X-Region': [ '{{#systemValue.env}}APP_REGION{{/systemValue.env}}' ] }, 'body': 'version {{#systemValue.property}}app.version{{/systemValue.property}}' }"
        )
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "path": "/region"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'X-Region': [ '{{#systemValue.env}}APP_REGION{{/systemValue.env}}' ] }, 'body': 'version {{#systemValue.property}}app.version{{/systemValue.property}}' }",
        "templateType": "MUSTACHE"
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

import requests

requests.put("http://localhost:1080/mockserver/expectation", json={
    "httpRequest": {
        "path": "/region"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'X-Region': [ '{{#systemValue.env}}APP_REGION{{/systemValue.env}}' ] }, 'body': 'version {{#systemValue.property}}app.version{{/systemValue.property}}' }",
        "templateType": "MUSTACHE"
    }
})
require 'net/http'

uri = URI('http://localhost:1080/mockserver/expectation')
Net::HTTP.start(uri.host, uri.port) do |http|
  http.send_request('PUT', uri.path, <<~'JSON', 'Content-Type' => 'application/json')
    {
        "httpRequest": {
            "path": "/region"
        },
        "httpResponseTemplate": {
            "template": "{ 'statusCode': 200, 'headers': { 'X-Region': [ '{{#systemValue.env}}APP_REGION{{/systemValue.env}}' ] }, 'body': 'version {{#systemValue.property}}app.version{{/systemValue.property}}' }",
            "templateType": "MUSTACHE"
        }
    }
  JSON
end
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
    "httpRequest": {
        "path": "/region"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'X-Region': [ '{{#systemValue.env}}APP_REGION{{/systemValue.env}}' ] }, 'body': 'version {{#systemValue.property}}app.version{{/systemValue.property}}' }",
        "templateType": "MUSTACHE"
    }
}`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""path"": ""/region""
    },
    ""httpResponseTemplate"": {
        ""template"": ""{ 'statusCode': 200, 'headers': { 'X-Region': [ '{{#systemValue.env}}APP_REGION{{/systemValue.env}}' ] }, 'body': 'version {{#systemValue.property}}app.version{{/systemValue.property}}' }"",
        ""templateType"": ""MUSTACHE""
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "path": "/region"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'X-Region': [ '{{#systemValue.env}}APP_REGION{{/systemValue.env}}' ] }, 'body': 'version {{#systemValue.property}}app.version{{/systemValue.property}}' }",
        "templateType": "MUSTACHE"
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "path": "/region"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'X-Region': [ '{{#systemValue.env}}APP_REGION{{/systemValue.env}}' ] }, 'body': 'version {{#systemValue.property}}app.version{{/systemValue.property}}' }",
        "templateType": "MUSTACHE"
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "path": "/region"
    },
    "httpResponseTemplate": {
        "template": "{ '\''statusCode'\'': 200, '\''headers'\'': { '\''X-Region'\'': [ '\''{{#systemValue.env}}APP_REGION{{/systemValue.env}}'\'' ] }, '\''body'\'': '\''version {{#systemValue.property}}app.version{{/systemValue.property}}'\'' }",
        "templateType": "MUSTACHE"
    }
}'

See REST API for full JSON specification

new ClientAndServer(1080)
    .when(request().withPath("/region"))
    .respond(
        template(
            HttpTemplate.TemplateType.VELOCITY,
            "{ 'statusCode': 200, 'headers': { 'X-Region': [ '$systemValue.env('APP_REGION')' ] }, 'body': 'version $systemValue.property('app.version')' }"
        )
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "path": "/region"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'X-Region': [ '$systemValue.env('APP_REGION')' ] }, 'body': 'version $systemValue.property('app.version')' }",
        "templateType": "VELOCITY"
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

import requests

requests.put("http://localhost:1080/mockserver/expectation", json={
    "httpRequest": {
        "path": "/region"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'X-Region': [ '$systemValue.env('APP_REGION')' ] }, 'body': 'version $systemValue.property('app.version')' }",
        "templateType": "VELOCITY"
    }
})
require 'net/http'

uri = URI('http://localhost:1080/mockserver/expectation')
Net::HTTP.start(uri.host, uri.port) do |http|
  http.send_request('PUT', uri.path, <<~'JSON', 'Content-Type' => 'application/json')
    {
        "httpRequest": {
            "path": "/region"
        },
        "httpResponseTemplate": {
            "template": "{ 'statusCode': 200, 'headers': { 'X-Region': [ '$systemValue.env('APP_REGION')' ] }, 'body': 'version $systemValue.property('app.version')' }",
            "templateType": "VELOCITY"
        }
    }
  JSON
end
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
    "httpRequest": {
        "path": "/region"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'X-Region': [ '$systemValue.env('APP_REGION')' ] }, 'body': 'version $systemValue.property('app.version')' }",
        "templateType": "VELOCITY"
    }
}`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""path"": ""/region""
    },
    ""httpResponseTemplate"": {
        ""template"": ""{ 'statusCode': 200, 'headers': { 'X-Region': [ '$systemValue.env('APP_REGION')' ] }, 'body': 'version $systemValue.property('app.version')' }"",
        ""templateType"": ""VELOCITY""
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "path": "/region"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'X-Region': [ '$systemValue.env('APP_REGION')' ] }, 'body': 'version $systemValue.property('app.version')' }",
        "templateType": "VELOCITY"
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "path": "/region"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'headers': { 'X-Region': [ '$systemValue.env('APP_REGION')' ] }, 'body': 'version $systemValue.property('app.version')' }",
        "templateType": "VELOCITY"
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "path": "/region"
    },
    "httpResponseTemplate": {
        "template": "{ '\''statusCode'\'': 200, '\''headers'\'': { '\''X-Region'\'': [ '\''$systemValue.env('\''APP_REGION'\'')'\'' ] }, '\''body'\'': '\''version $systemValue.property('\''app.version'\'')'\'' }",
        "templateType": "VELOCITY"
    }
}'

See REST API for full JSON specification

new ClientAndServer(1080)
    .when(request().withPath("/region"))
    .respond(
        template(
            HttpTemplate.TemplateType.JAVASCRIPT,
            "return { 'statusCode': 200, 'headers': { 'X-Region': [ systemValue.env('APP_REGION') ] }, 'body': 'version ' + systemValue.property('app.version') };"
        )
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "path": "/region"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 200, 'headers': { 'X-Region': [ systemValue.env('APP_REGION') ] }, 'body': 'version ' + systemValue.property('app.version') };",
        "templateType": "JAVASCRIPT"
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

import requests

requests.put("http://localhost:1080/mockserver/expectation", json={
    "httpRequest": {
        "path": "/region"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 200, 'headers': { 'X-Region': [ systemValue.env('APP_REGION') ] }, 'body': 'version ' + systemValue.property('app.version') };",
        "templateType": "JAVASCRIPT"
    }
})
require 'net/http'

uri = URI('http://localhost:1080/mockserver/expectation')
Net::HTTP.start(uri.host, uri.port) do |http|
  http.send_request('PUT', uri.path, <<~'JSON', 'Content-Type' => 'application/json')
    {
        "httpRequest": {
            "path": "/region"
        },
        "httpResponseTemplate": {
            "template": "return { 'statusCode': 200, 'headers': { 'X-Region': [ systemValue.env('APP_REGION') ] }, 'body': 'version ' + systemValue.property('app.version') };",
            "templateType": "JAVASCRIPT"
        }
    }
  JSON
end
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
    "httpRequest": {
        "path": "/region"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 200, 'headers': { 'X-Region': [ systemValue.env('APP_REGION') ] }, 'body': 'version ' + systemValue.property('app.version') };",
        "templateType": "JAVASCRIPT"
    }
}`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""path"": ""/region""
    },
    ""httpResponseTemplate"": {
        ""template"": ""return { 'statusCode': 200, 'headers': { 'X-Region': [ systemValue.env('APP_REGION') ] }, 'body': 'version ' + systemValue.property('app.version') };"",
        ""templateType"": ""JAVASCRIPT""
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "path": "/region"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 200, 'headers': { 'X-Region': [ systemValue.env('APP_REGION') ] }, 'body': 'version ' + systemValue.property('app.version') };",
        "templateType": "JAVASCRIPT"
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "path": "/region"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 200, 'headers': { 'X-Region': [ systemValue.env('APP_REGION') ] }, 'body': 'version ' + systemValue.property('app.version') };",
        "templateType": "JAVASCRIPT"
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "path": "/region"
    },
    "httpResponseTemplate": {
        "template": "return { '\''statusCode'\'': 200, '\''headers'\'': { '\''X-Region'\'': [ systemValue.env('\''APP_REGION'\'') ] }, '\''body'\'': '\''version '\'' + systemValue.property('\''app.version'\'') };",
        "templateType": "JAVASCRIPT"
    }
}'

See REST API for full JSON specification

 

Your Own Helpers (ext)

An extension can add its own helpers, under ext.<extension name>. They can be called from a response template (httpResponseTemplate) or a forward template (httpForwardTemplate) only; any other template that calls one (a webhook, a secondary action, a delay, a templated file body, a stream, a load scenario or the dry run) fails with an error that says so. Without such an extension, ext is not defined and templates behave exactly as before.

  • Velocity: $ext.acme.Strings.upper($request.path)
  • JavaScript: ext.acme.Strings.upper(request.path)
  • Mustache: {{#ext.acme.upper}}{{request.path}}{{/ext.acme.upper}} (the section is rendered first and the helper receives the result)

All the helper calls one template makes share one time budget, extensionHelperTimeoutMillis (default 1 second): a call still running when it is used up is stopped, and the template fails the same way as any other failing template (see When a Template Fails or Runs Too Long). A name that does not exist, such as a mistyped helper, also fails the template rather than rendering as written.

 

Mustache Response Templates

The following shows a basic example for a mustache format response template

new ClientAndServer(1080)
    .when(request().withPath("/some/path"))
    .respond(
        template(
            HttpTemplate.TemplateType.MUSTACHE,
            "{\n" +
                "     'statusCode': 200,\n" +
                "     'cookies': {\n" +
                "          'session': '{{ request.headers.Session-Id.0 }}'\n" +
                "     },\n" +
                "     'headers': {\n" +
                "          'Client-User-Agent': [ '{{ request.headers.User-Agent.0 }}' ]\n" +
                "     },\n" +
                "     'body': '{{ request.body }}'\n" +
                "}"
        )
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "path": "/some/path"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'cookies': { 'session': '{{ request.headers.Session-Id.0 }}' }, 'headers': { 'Client-User-Agent': [ '{{ request.headers.User-Agent.0 }}' ] }, 'body': '{{ request.body }}' }",
        "templateType": "MUSTACHE"
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "path": "/some/path"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'cookies': { 'session': '{{ request.headers.Session-Id.0 }}' }, 'headers': { 'Client-User-Agent': [ '{{ request.headers.User-Agent.0 }}' ] }, 'body': '{{ request.body }}' }",
        "templateType": "MUSTACHE"
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    'httpRequest' => {
        'path' => '/some/path'
    },
    'httpResponseTemplate' => {
        'template' => "{ 'statusCode': 200, 'cookies': { 'session': '{{ request.headers.Session-Id.0 }}' }, 'headers': { 'Client-User-Agent': [ '{{ request.headers.User-Agent.0 }}' ] }, 'body': '{{ request.body }}' }",
        'templateType' => 'MUSTACHE'
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
    "httpRequest": {
        "path": "/some/path"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'cookies': { 'session': '{{ request.headers.Session-Id.0 }}' }, 'headers': { 'Client-User-Agent': [ '{{ request.headers.User-Agent.0 }}' ] }, 'body': '{{ request.body }}' }",
        "templateType": "MUSTACHE"
    }
}`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""path"": ""/some/path""
    },
    ""httpResponseTemplate"": {
        ""template"": ""{ 'statusCode': 200, 'cookies': { 'session': '{{ request.headers.Session-Id.0 }}' }, 'headers': { 'Client-User-Agent': [ '{{ request.headers.User-Agent.0 }}' ] }, 'body': '{{ request.body }}' }"",
        ""templateType"": ""MUSTACHE""
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "path": "/some/path"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'cookies': { 'session': '{{ request.headers.Session-Id.0 }}' }, 'headers': { 'Client-User-Agent': [ '{{ request.headers.User-Agent.0 }}' ] }, 'body': '{{ request.body }}' }",
        "templateType": "MUSTACHE"
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "path": "/some/path"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'cookies': { 'session': '{{ request.headers.Session-Id.0 }}' }, 'headers': { 'Client-User-Agent': [ '{{ request.headers.User-Agent.0 }}' ] }, 'body': '{{ request.body }}' }",
        "templateType": "MUSTACHE"
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "path": "/some/path"
    },
    "httpResponseTemplate": {
        "template": "{ '\''statusCode'\'': 200, '\''cookies'\'': { '\''session'\'': '\''{{ request.headers.Session-Id.0 }}'\'' }, '\''headers'\'': { '\''Client-User-Agent'\'': [ '\''{{ request.headers.User-Agent.0 }}'\'' ] }, '\''body'\'': '\''{{ request.body }}'\'' }",
        "templateType": "MUSTACHE"
    }
}'

See REST API for full JSON specification

Mustache Syntax

Variables

In mustache a variable is referenced using double braces, for example, {{ request.method }} will print the method field of the request variable.

{{ name }} will try to find the name key in the current model context; which can be changed, using sections, as described below.

If no matching field is found an empty string will be returned.

The following basic template example demonstrates using multiple variables:

{
  'statusCode': 200,
  'body': {
    'method': '{{ request.method }}',
    'path': '{{ request.path }}',
    'headers': '{{ request.headers.host.0 }}'
  }
}

Sections

Sections have the following uses:

  • to change the model context
  • as an if-else-statement
  • to loop over arrays or iterables

A section begins with a tag starting with a pound and ends with a matching tag starting with a slash.

For example {{#request.cookies}} starts a request.cookies section and {{/request.cookies}} closes the section.

How a section behaves depends on the section's model variable as follows

  • boolean values enable or disable the section like an if-statement; with unresolvable variables, null variables or empty strings all being treated as false
  • arrays, iterators, and iterable variables cause the section to loop over each item; with empty collections never executing the section
  • other objects result in a single execution of the section using the section's model variable as the context

The following example template demonstrates using a section to iterate the entrySet in the header map:

{
  'statusCode': 200,
  'body': "{'headers': '{{#request.headers.entrySet}}{{ key }}={{ value.0 }} {{/request.headers.entrySet}}'}"
}

And produces the following example output:

{
  "statusCode" : 200,
  "body" : "{'headers': 'host=mock-server.com content-type=plain/text '}"
}

Inverted Sections

An inverted section begins with a tag starting with a caret and ends with a matching tag starting with a slash.

For example {{^-first}} starts a -first section and {{/-first}} closes the section. This section therefore applies for all items in a list except the first item.

Inverted sections render once based on the inverse value of the key, they will be rendered if the key doesn't exist, is false, is an empty string, or is an empty list.

The following example template demonstrates using a section to iterate the entrySet in the header map using an inverted section and the special variable -first to add commas before all headers except the first:

{
  'statusCode': 200,
  'body': "{'headers': [{{#request.headers.entrySet}}{{^-first}}, {{/-first}}'{{ key }}={{ value.0 }}'{{/request.headers.entrySet}}]}"
}

And produces the following example output:

{
  "statusCode" : 200,
  "body" : "{'headers': ['host=mock-server.com', 'content-type=plain/text']}"
}

Special Variables

this

this refers to the context object itself such as the current item when iterating over a collection

-first and -last

You can use the special variables -first and -last when using collections.

-first resolves to true when inside a section on the first iteration, it resolves to false at all other times. It resolves to false for sections with a singleton value rather a collection.

-last resolves to true when inside a section on the last iteration, it resolves to false at all other times.

-index

The -index special variable resolves to 1 for the first iteration, 2 for the second so on. It resolves to 0 at all other times including sections with a singleton value rather a collection.

The following example template demonstrates using this and -first to list header values

{
    'statusCode': 200,
    'body': "{'headers': [{{#request.headers.values}}{{^-first}}, {{/-first}}'{{ this.0 }}'{{/request.headers.values}}]}"
}

And produces the following example output:

{
  "statusCode" : 200,
  "body" : "{'headers': ['mock-server.com', 'plain/text']}"
}

The following example template demonstrates using this, -first and -index to list header keys

{
    'statusCode': 200,
    'body': "{'headers': [{{#request.headers.keySet}}{{^-first}}, {{/-first}}'{{ -index }}:{{ this }}'{{/request.headers.keySet}}]}"
}

And produces the following example output:

{
  "statusCode" : 200,
  "body" : "{'headers': ['1:host', '2:content-type']}"
}
 

JsonPath

It is possible to use JsonPath expressions to extract values from request bodies containing JSON with a {{#jsonPath}} section, follow by a {{#jsonPathResult}} section

For details of the full JsonPath syntax please see github.com/json-path

The following example template demonstrates using {{#jsonPath}} follow by {{#jsonPathResult}} to extract a list and single value from a request body:

{
    'statusCode': 200,
    'body': "{'titles': {{#jsonPath}}$.store.book{{/jsonPath}}[{{#jsonPathResult}}{{^-first}}, {{/-first}}'{{title}}'{{/jsonPathResult}}], 'bikeColor': '{{#jsonPath}}$.store.bicycle.color{{/jsonPath}}{{jsonPathResult}}'}"
}

In this example the first JsonPath expression $.store.book returns a list of objects which is iterated over in the section {{#jsonPathResult}}.

The second JsonPath expression $.store.bicycle.color returns a single value which is returned using the variable tag {{jsonPathResult}}.

Given the following request:

{
  "path" : "/somePath",
  "body" : {
    "store" : {
      "book" : [ {
        "category" : "reference",
        "author" : "Nigel Rees",
        "title" : "Sayings of the Century",
        "price" : 18.95
      }, {
        "category" : "fiction",
        "author" : "Herman Melville",
        "title" : "Moby Dick",
        "isbn" : "0-553-21311-3",
        "price" : 8.99
      } ],
      "bicycle" : {
        "color" : "red",
        "price" : 19.95
      }
    },
    "expensive" : 10
  }
}

The example produces:

{
  "statusCode" : 200,
  "body" : "{'titles': ['Sayings of the Century', 'Moby Dick'], 'bikeColor': 'red'}"
}

XPath

It is possible to use XPath expressions to extract values from request bodies containing XML with a {{#xPath}} section.

The {{#xPath}} section only supports outputting the result of the XPath expression as a string, if an XML fragment is matched the string content of all the elements is printed.

For a quick summary the XPath syntax please see w3schools, for details of the full XPath syntax please see www.w3.org

The following example template demonstrates using {{#xPath}} to multiple items from a request body:

{
    'statusCode': 200,
    'body': "{'titles': ['{{#xPath}}/store/book/title{{/xPath}}', '{{#xPath}}//book[2]/title{{/xPath}}'], 'bikeColor': '{{#xPath}}//bicycle/color{{/xPath}}'}"
}

Given a request with the following xml body:

<?xml version="1.0" encoding="UTF-8" ?>
<store>
  <book>
    <category>reference</category>
    <author>Nigel Rees</author>
    <title>Sayings of the Century</title>
    <price>18.95</price>
  </book>
  <book>
    <category>fiction</category>
    <author>Herman Melville</author>
    <title>Moby Dick</title>
    <isbn>0-553-21311-3</isbn>
    <price>8.99</price>
  </book>
  <bicycle>
    <color>red</color>
    <price>19.95</price>
  </bicycle>
  <expensive>10</expensive>
</store>

The example produces:

{
  "statusCode" : 200,
  "body" : "{'titles': ['Sayings of the Century', 'Moby Dick'], 'bikeColor': 'red'}"
}
 

Velocity Response Templates

The following shows a basic example for a Velocity format response template

new ClientAndServer(1080)
    .when(request().withPath("/some/path"))
    .respond(
        template(
            HttpTemplate.TemplateType.VELOCITY,
            "{\n" +
                "     'statusCode': 200,\n" +
                "     'cookies': { \n" +
                "          'session': '$!request.headers['Session-Id'][0]'\n" +
                "     },\n" +
                "     'headers': {\n" +
                "          'Client-User-Agent': [ '$!request.headers['User-Agent'][0]' ]\n" +
                "     },\n" +
                "     'body': '$!request.body'\n" +
                "}"
        )
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "path": "/some/path"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'cookies': { 'session': '$!request.headers['Session-Id'][0]' }, 'headers': { 'Client-User-Agent': [ '$!request.headers['User-Agent'][0]' ] }, 'body': '$!request.body' }",
        "templateType": "VELOCITY"
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "path": "/some/path"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'cookies': { 'session': '$!request.headers['Session-Id'][0]' }, 'headers': { 'Client-User-Agent': [ '$!request.headers['User-Agent'][0]' ] }, 'body': '$!request.body' }",
        "templateType": "VELOCITY"
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    'httpRequest' => {
        'path' => '/some/path'
    },
    'httpResponseTemplate' => {
        'template' => "{ 'statusCode': 200, 'cookies': { 'session': '$!request.headers['Session-Id'][0]' }, 'headers': { 'Client-User-Agent': [ '$!request.headers['User-Agent'][0]' ] }, 'body': '$!request.body' }",
        'templateType' => 'VELOCITY'
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
    "httpRequest": {
        "path": "/some/path"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'cookies': { 'session': '$!request.headers['Session-Id'][0]' }, 'headers': { 'Client-User-Agent': [ '$!request.headers['User-Agent'][0]' ] }, 'body': '$!request.body' }",
        "templateType": "VELOCITY"
    }
}`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""path"": ""/some/path""
    },
    ""httpResponseTemplate"": {
        ""template"": ""{ 'statusCode': 200, 'cookies': { 'session': '$!request.headers['Session-Id'][0]' }, 'headers': { 'Client-User-Agent': [ '$!request.headers['User-Agent'][0]' ] }, 'body': '$!request.body' }"",
        ""templateType"": ""VELOCITY""
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "path": "/some/path"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'cookies': { 'session': '$!request.headers['Session-Id'][0]' }, 'headers': { 'Client-User-Agent': [ '$!request.headers['User-Agent'][0]' ] }, 'body': '$!request.body' }",
        "templateType": "VELOCITY"
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "path": "/some/path"
    },
    "httpResponseTemplate": {
        "template": "{ 'statusCode': 200, 'cookies': { 'session': '$!request.headers['Session-Id'][0]' }, 'headers': { 'Client-User-Agent': [ '$!request.headers['User-Agent'][0]' ] }, 'body': '$!request.body' }",
        "templateType": "VELOCITY"
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "path": "/some/path"
    },
    "httpResponseTemplate": {
        "template": "{ '\''statusCode'\'': 200, '\''cookies'\'': { '\''session'\'': '\''$!request.headers['\''Session-Id'\''][0]'\'' }, '\''headers'\'': { '\''Client-User-Agent'\'': [ '\''$!request.headers['\''User-Agent'\''][0]'\'' ] }, '\''body'\'': '\''$!request.body'\'' }",
        "templateType": "VELOCITY"
    }
}'

See REST API for full JSON specification

Velocity Syntax

For full Velocity syntax see: Velocity User Guide the following section provide a basic summary and examples of Velocity syntax.

Variables

In velocity a variable is referenced using a dollar, for example, $request.method will print the method field of the request variable.

$name will try to find the name variable in the current model.

If no matching variable is found for the variable expression the expression is printed, unless a quiet expression notation is used as follows $!name

The following basic template example demonstrates using multiple variables:

{
  'statusCode': 200,
  'body': {
    'method': '$!request.method',
    'path': '$!request.path',
    'headers': '$!request.headers.host.0'
  }
}

Methods can also be called on variables, for example, $request.getMethod() will call the getMethod() method of the request variable.

New variables can be defined using a #set directive, for example, #set ($message="Hello World") will create a new variables call message.

Conditionals

Conditional expressions are possible using #if, #elseif and #else directives, as follows:

#if($request.method == 'POST' && $request.path == '/somePath')
    {
        'statusCode': 200,
        'body': "{'name': 'value'}"
    }
#else
    {
        'statusCode': 406,
        'body': "$!request.body"
    }
#end

Loops

Loops are possible using the #foreach directives, as follows:

{
    'statusCode': 200,
    'body': "{'headers': [#foreach( $value in $request.headers.values() )'$value[0]'#if( $foreach.hasNext ), #end#end]}"
}

The example produces:

{
  "statusCode" : 200,
  "body" : "{'headers': ['mock-server.com', 'plain/text']}"
}

Mathematical

In addition to the dynamic model variables it is possible to perform basic mathematical operations, as follows:

#set($percent = $number / 100)
#set($remainder = $dividend % $divisor)

A range operator is also supported which can be useful in conjunction with #set and #foreach

#set($array = [0..10])
#foreach($item in $arr)
    $item
#end

For additional mathematical functionality it is also possible to use the MathTool or NumberTool in velocity response templates.

#set($power = $math.pow($number, 2))
#set($max = $math.max($number, 10))

Json Bodies

The JsonTool can be used to help parse JSON bodies, as follows:

#set($jsonBody = $json.parse($!request.body))

{
    'statusCode': 200,
    'body': "{'titles': [#foreach( $book in $jsonBody.store.book )'$book.title'#if( $foreach.hasNext ), #end#end], 'bikeColor': '$jsonBody.store.bicycle.color'}"
}

Given the following request:

{
  "path" : "/somePath",
  "body" : {
    "type" : "JSON",
    "json" : {
      "store" : {
        "book" : [ {
          "category" : "reference",
          "author" : "Nigel Rees",
          "title" : "Sayings of the Century",
          "price" : 18.95
        }, {
          "category" : "fiction",
          "author" : "Herman Melville",
          "title" : "Moby Dick",
          "isbn" : "0-553-21311-3",
          "price" : 8.99
        } ],
        "bicycle" : {
          "color" : "red",
          "price" : 19.95
        }
      },
      "expensive" : 10
    }
  }
}

The example produces:

{
  "statusCode" : 200,
  "body" : "{'titles': ['Sayings of the Century', 'Moby Dick'], 'bikeColor': 'red'}"
}

XML Bodies

The XmlTool can be used to help parse XML bodies, execute XPath and XML traversal.

The following example shows how to use XmlTool for XPath:

#set($xmlBody = $xml.parse($!request.body))

{
    'statusCode': 200,
    'body': "{'key': '$xml.find('/element/key/text()')', 'value': '$xml.find('/element/value/text()')'}"
}

Given the following request:

{
  "path" : "/somePath",
  "body" : "<element><key>some_key</key><value>some_value</value></element>"
}

The example produces:

{
  "statusCode" : 200,
  "body" : "{'key': 'some_key', 'value': 'some_value'}"
}

JsonPath and XPath helpers

As a quicker alternative to $xml.parse() / $json.parse(), Velocity templates can extract a single value directly from the request body using the same JsonPath and XPath support as Mustache templates, via the $jsonPath and $xPath helpers:

{
    'statusCode': 200,
    'body': "{'title': '$jsonPath.find('$.store.book[0].title')', 'key': '$xPath.find('/element/key')'}"
}

$jsonPath.find(...) evaluates a JsonPath expression against a JSON request body and $xPath.find(...) evaluates an XPath expression against an XML request body. A missing path or invalid expression resolves to an empty value (the error is logged, not thrown), exactly as in Mustache templates.

Velocity Tools

The following velocity tools are available for velocity response templates:

 

JavaScript Response Templates

The following shows a basic example for a JavaScript format response template

new ClientAndServer(1080)
    .when(request().withPath("/some/path"))
    .respond(
        template(
            HttpTemplate.TemplateType.JAVASCRIPT,
            "return {\n" +
                "     'statusCode': 200,\n" +
                "     'cookies': {\n" +
                "          'session' : request.headers['session-id'][0]\n" +
                "     },\n" +
                "     'headers': {\n" +
                "          'Client-User-Agent': request.headers['User-Agent']\n" +
                "     },\n" +
                "     'body': request.body\n" +
                "};"
        )
    );
var mockServerClient = require('mockserver-client').mockServerClient;
mockServerClient("localhost", 1080).mockAnyResponse({
    "httpRequest": {
        "path": "/some/path"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 200, 'cookies': { 'session' : request.headers['session-id'][0] }, 'headers': { 'Client-User-Agent': request.headers['User-Agent'] }, 'body': request.body };",
        "templateType": "JAVASCRIPT"
    }
}).then(
    function () {
        console.log("expectation created");
    },
    function (error) {
        console.log(error);
    }
);

See REST API for full JSON specification

from mockserver.client import MockServerClient
from mockserver.models import Expectation

client = MockServerClient("localhost", 1080)
client.upsert(Expectation.from_dict({
    "httpRequest": {
        "path": "/some/path"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 200, 'cookies': { 'session' : request.headers['session-id'][0] }, 'headers': { 'Client-User-Agent': request.headers['User-Agent'] }, 'body': request.body };",
        "templateType": "JAVASCRIPT"
    }
}))
require 'mockserver-client'

client = MockServer::Client.new('localhost', 1080)
client.upsert(MockServer::Expectation.from_hash({
    'httpRequest' => {
        'path' => '/some/path'
    },
    'httpResponseTemplate' => {
        'template' => "return { 'statusCode': 200, 'cookies': { 'session' : request.headers['session-id'][0] }, 'headers': { 'Client-User-Agent': request.headers['User-Agent'] }, 'body': request.body };",
        'templateType' => 'JAVASCRIPT'
    }
}))
package main

import (
    "bytes"
    "net/http"
)

func main() {
    body := []byte(`{
    "httpRequest": {
        "path": "/some/path"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 200, 'cookies': { 'session' : request.headers['session-id'][0] }, 'headers': { 'Client-User-Agent': request.headers['User-Agent'] }, 'body': request.body };",
        "templateType": "JAVASCRIPT"
    }
}`)
    req, _ := http.NewRequest("PUT",
        "http://localhost:1080/mockserver/expectation",
        bytes.NewReader(body))
    req.Header.Set("Content-Type", "application/json")
    http.DefaultClient.Do(req)
}
using System.Net.Http;
using System.Text;

using var httpClient = new HttpClient();
var json = @"{
    ""httpRequest"": {
        ""path"": ""/some/path""
    },
    ""httpResponseTemplate"": {
        ""template"": ""return { 'statusCode': 200, 'cookies': { 'session' : request.headers['session-id'][0] }, 'headers': { 'Client-User-Agent': request.headers['User-Agent'] }, 'body': request.body };"",
        ""templateType"": ""JAVASCRIPT""
    }
}";
await httpClient.PutAsync(
    "http://localhost:1080/mockserver/expectation",
    new StringContent(json, Encoding.UTF8, "application/json")
);
use reqwest::blocking::Client;

let client = Client::new();
let body = r#"{
    "httpRequest": {
        "path": "/some/path"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 200, 'cookies': { 'session' : request.headers['session-id'][0] }, 'headers': { 'Client-User-Agent': request.headers['User-Agent'] }, 'body': request.body };",
        "templateType": "JAVASCRIPT"
    }
}"#;
client.put("http://localhost:1080/mockserver/expectation")
    .header("Content-Type", "application/json")
    .body(body.to_string())
    .send()
    .unwrap();
$json = <<<'JSON'
{
    "httpRequest": {
        "path": "/some/path"
    },
    "httpResponseTemplate": {
        "template": "return { 'statusCode': 200, 'cookies': { 'session' : request.headers['session-id'][0] }, 'headers': { 'Client-User-Agent': request.headers['User-Agent'] }, 'body': request.body };",
        "templateType": "JAVASCRIPT"
    }
}
JSON;

$ch = curl_init('http://localhost:1080/mockserver/expectation');
curl_setopt_array($ch, [
    CURLOPT_CUSTOMREQUEST  => 'PUT',
    CURLOPT_POSTFIELDS     => $json,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json; charset=utf-8'],
    CURLOPT_RETURNTRANSFER => true,
]);
curl_exec($ch);
curl_close($ch);
curl -v -X PUT "http://localhost:1080/mockserver/expectation" -d '{
    "httpRequest": {
        "path": "/some/path"
    },
    "httpResponseTemplate": {
        "template": "return { '\''statusCode'\'': 200, '\''cookies'\'': { '\''session'\'' : request.headers['\''session-id'\''][0] }, '\''headers'\'': { '\''Client-User-Agent'\'': request.headers['\''User-Agent'\''] }, '\''body'\'': request.body };",
        "templateType": "JAVASCRIPT"
    }
}'

See REST API for full JSON specification

JavaScript Syntax

All javascript response templates should return the response as an object as shown in the example above.

JavaScript Engine Support

MockServer uses the GraalVM Polyglot JavaScript engine for JavaScript templates. The engine is not bundled with the standard Docker image or the standalone JAR — only the text-based Mustache and Velocity engines are available by default. To use JavaScript templates, either run the graaljs Docker image variant (e.g. mockserver/mockserver:9.2.0-graaljs) or add org.graalvm.polyglot:polyglot and org.graalvm.polyglot:js to your classpath (see below). It supports ES2023+ syntax, including template literals, async/await, destructuring, optional chaining, and the full modern JavaScript language.

If a JAVASCRIPT template is used when the GraalJS engine is not on the classpath, the request now fails with a clear error rather than silently returning a degraded/empty response: "JavaScript response templates require the GraalJS engine, which is not on the classpath. Add the org.graalvm.polyglot:js (or js-community) dependency, or use the Velocity or Mustache template engine." Add the GraalJS dependency (or switch to Velocity/Mustache) to resolve it.

If you are embedding MockServer as a library and need JavaScript templating, add org.graalvm.polyglot:polyglot and org.graalvm.polyglot:js (both at version 25.0.3 or later) to your classpath. Nashorn (org.openjdk.nashorn:nashorn-core) is no longer supported; the JSR-223 bridge was dropped in GraalJS 25.x and Nashorn was removed from MockServer.

Variables

In javascript a variable is referenced directly, for example, request.method will return the method field of the request variable.

The following basic template example demonstrates using multiple variables:

return {
    'statusCode': 200,
    'body': '{\'method\': \'' + request.method + '\', \'path\': \'' + request.path + '\', \'header\': \'' + request.headers.host[0] + '\'}'
};

JsonPath and XPath helpers

As an alternative to JSON.parse(request.body), JavaScript templates can extract a single value directly from the request body using the same JsonPath and XPath support as Mustache templates, via the jsonPath(...) and xPath(...) functions:

return {
    'statusCode': 200,
    'body': '{\'title\': \'' + jsonPath('$.store.book[0].title') + '\', \'key\': \'' + xPath('/element/key') + '\'}'
};

jsonPath(...) evaluates a JsonPath expression against a JSON request body and xPath(...) evaluates an XPath expression against an XML request body. A missing path or invalid expression returns an empty value (the error is logged, not thrown), exactly as in Mustache templates.

Conditionals

Conditional expressions are possible, as follows:

if (request.method === 'POST' && request.path === '/somePath') {
    return {
        'statusCode': 200,
        'body': JSON.stringify({name: 'value'})
    };
} else {
    return {
        'statusCode': 406,
        'body': request.body
    };
}

Loops

Looping is possible, as follows:

var headers = '';
for (header in request.headers) {
  headers += '\'' + request.headers[header] + '\', ';
}
return {
    'statusCode': 200,
    'body': '{\'headers\': [' + headers.slice(0, -2) + ']}'
};

The example produces:

{
  "statusCode" : 200,
  "body" : "{'headers': ['mock-server.com', 'plain/text']}"
}
 

When a Template Fails or Runs Too Long

Time limits. A JavaScript template is stopped after javascriptTemplateExecutionTimeout milliseconds. A Velocity template is stopped after velocityTemplateExecutionTimeout milliseconds at its next #foreach iteration, macro call, #parse, #evaluate or use of a #define block, so a runaway loop, recursive macro or chain of blocks fails instead of tying up the server. Both default to 5000 (5 seconds), far longer than a normal template needs. Set either to 0 to remove the limit. A single slow Java call inside a Velocity template, such as a regular expression in $s.matches(...) that backtracks heavily or a method that walks a very large range, cannot be stopped part-way and runs until it finishes. The limit is on time, not memory. Mustache has no loops of its own and needs no limit.

What the client receives. By default a template that fails (it throws an error, does not parse, or hits its time limit) answers 500 Internal Server Error with an x-mockserver-template-error: true header and the template error as a plain-text body, which names the engine and gives its error, followed by the template line and column, and a hint for common mistakes, when the engine reports them, for example VELOCITY response template failed: Encountered ... at VelocityResponseTemplate[line 1, column 10] then at template line 1, column 10 for a syntax error. The log always records the template error. The body carries the engine's error message, which can quote part of the template or its output, so if the callers of your mock should not see template details, set -Dmockserver.templateFailureAsServerError=false: a failed template then answers 404 Not Found, the same status as a request that matched no expectation, as it did before MockServer 9.0.0.

Output that is not a valid response. A template that runs but produces output that is not a valid response (for example a statusCode that is not a number) answers the same 500, whose body lists what is wrong with the output, and so does a forward template whose output is not a valid request. For a response template the body starts VELOCITY response template rendered output that is not a valid response:, followed by errors such as $.statusCode: string found, integer expected. For a forward template it starts VELOCITY forward template rendered output that is not a valid request:. The engine name changes with the template type (VELOCITY, JAVASCRIPT or MUSTACHE). The same applies to the response template of a forward with overrides, which renders when the upstream response arrives: a failure there, or output that is not valid, answers the 500. With templateFailureAsServerError=false, a response template whose output is not valid answers 404, a forward template whose output is not a valid request answers 502 Bad Gateway, a forward's response template that fails answers 502, and one whose output is not valid passes the upstream response through unchanged.

 

Security Considerations

Response templates execute code. A template that can reach classes such as java.lang.Runtime or java.lang.ProcessBuilder can run OS commands inside the MockServer process, so anyone able to register an expectation could run code on the machine MockServer is running on.

Templates cannot reach Java classes out of the box, so this is safe by default and you do not need to configure anything:

  • Velocity — velocityDisallowClassLoading now defaults to true, so class loading from within a Velocity template (for example $request.class.classLoader.loadClass(...)) is blocked.
  • JavaScript — a JavaScript template resolves no Java class unless you name it in javascriptAllowedClasses. Java.type(...) and the java.* globals return nothing until then; the rest of the language is unaffected.

Everything templates are normally used for still works: request values, the built-in helpers ($uuid, $now, $faker, JWT, string, date and math helpers), the $json/$xml tools, and the whole modern JavaScript standard library.

If a template of yours genuinely needs a Java class, grant the minimum it needs:

  • JavaScript — -Dmockserver.javascriptAllowedClasses="java.util.*,java.time.Instant". Use * to let templates resolve any class again, and only when every template comes from a source you fully trust.
  • Velocity — -Dmockserver.velocityDisallowClassLoading=false re-enables class loading for all Velocity templates, with the same caveat.

For JavaScript templates, a refused class is written to the log at WARN naming the class and the property to set, so a template that stops working tells you exactly what to grant. A Velocity template that is refused a class simply renders that part of the output empty.

With the default class restrictions, templates cannot read the environment variables or system properties of the MockServer process either (apart from the host name, through hostname), unless you name them in templateSystemValueAllowList for the systemValue helper. Anyone who can create expectations, call them or read the log can read the values of the names you list, so never list secrets; MockServer's own mockserver.* and MOCKSERVER_* settings are never readable.

See the configuration properties page for the full list of equivalent system-property, environment-variable, and Java API forms of these settings. To stop untrusted callers from registering templated expectations in the first place, also enable control plane authorisation.