{"id":2164,"date":"2026-03-14T01:43:47","date_gmt":"2026-03-13T12:43:47","guid":{"rendered":"https:\/\/www.ronella.xyz\/?p=2164"},"modified":"2026-03-14T01:43:47","modified_gmt":"2026-03-13T12:43:47","slug":"wiremock-response-templates-with-handlebars-and-response-builders","status":"publish","type":"post","link":"https:\/\/www.ronella.xyz\/?p=2164","title":{"rendered":"WireMock Response Templates with Handlebars and Response Builders"},"content":{"rendered":"<p>WireMock gives you two powerful levers for dynamic responses: declarative Handlebars templates embedded in stubs, and imperative Java logic via the response builder APIs. They complement each other rather than compete.<\/p>\n<hr \/>\n<h2>Why response templating exists<\/h2>\n<p>In real systems, responses rarely stay static: IDs echo from the URL, correlation IDs flow through headers, timestamps change, payloads depend on the request body, and so on. WireMock\u2019s response templating solves this by letting you:<\/p>\n<ul>\n<li>Use <strong>Handlebars<\/strong> to keep mocks close to the contract, with simple expressions and helpers.<\/li>\n<li>Drop to <strong>Java builders and transformers<\/strong> when behaviour becomes algorithmic or needs integration with other libraries.<\/li>\n<\/ul>\n<p>The design goal is: keep the <em>simple<\/em> cases in configuration, and only push <em>complex<\/em> behaviour into code.<\/p>\n<hr \/>\n<h2>Handlebars in WireMock: the declarative layer<\/h2>\n<p>Handlebars lets you embed <code>{{expressions}}<\/code> directly in response headers, bodies, and even proxy URLs. At runtime, WireMock feeds a rich <strong>request model<\/strong> into the template engine so the template can \u201csee\u201d all relevant request data.<\/p>\n<h2>The request model<\/h2>\n<p>Some of the most useful fields in the <code>request<\/code> model:<\/p>\n<ul>\n<li><code>request.url<\/code>, <code>request.path<\/code>, <code>request.pathSegments.[n]<\/code> and named path variables (e.g. <code>request.path.customerId<\/code>).<\/li>\n<li><code>request.query.foo<\/code> or <code>request.query.foo.[n]<\/code> for multi-valued params.<\/li>\n<li><code>request.headers.X-Request-Id<\/code>, <code>request.cookies.session<\/code>, <code>request.method<\/code>, <code>request.baseUrl<\/code>.<\/li>\n<li><code>request.body<\/code>, <code>request.bodyAsBase64<\/code>, and multipart <code>request.parts.*<\/code> for more advanced cases.<\/li>\n<\/ul>\n<p>Rationale: this model gives you enough context to \u201cshape\u201d the response purely from the incoming HTTP request, without Java code.<\/p>\n<h2>Enabling and scoping templating<\/h2>\n<p>In programmatic (local) mode, WireMock 3+ enables templating support out of the box, but actually <em>applying<\/em> it depends on how your instance is configured:<\/p>\n<ul>\n<li>You can run with <strong>local templating<\/strong>, where templating is applied only to stubs that specify the <code>response-template<\/code> transformer.<\/li>\n<li>Or you can flip to <strong>global templating<\/strong>, where every stub is rendered through Handlebars unless explicitly disabled.<\/li>\n<\/ul>\n<p>This is intentional: it prevents accidental template evaluation on basic static stubs, while still allowing an \u201call templates\u201d mode when you know your mapping set depends heavily on it.<\/p>\n<h2>Basic Handlebars example<\/h2>\n<p>Below is a minimal, self-contained Java example using a modern WireMock 3.x style API, focusing only on the stub and response (no build configuration). It echoes data from path, query, and headers via Handlebars.<\/p>\n<pre><code class=\"language-java\">import com.github.tomakehurst.wiremock.WireMockServer;\nimport com.github.tomakehurst.wiremock.core.WireMockConfiguration;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\n\npublic class HandlebarsExample {\n\n    public static void main(String[] args) {\n        WireMockServer wm = new WireMockServer(\n            WireMockConfiguration.options()\n                \/\/ Local templating enabled by default in Java mode;\n                \/\/ if you\u2019d changed it globally, you can toggle here.\n                .templatingEnabled(true)\n        );\n        wm.start();\n\n        configureFor(&quot;localhost&quot;, wm.port());\n\n        wm.stubFor(\n            get(urlPathMatching(&quot;\/customers\/(.*)&quot;))\n                .willReturn(\n                    aResponse()\n                        .withStatus(200)\n                        .withHeader(&quot;Content-Type&quot;, &quot;application\/json&quot;)\n                        \/\/ Handlebars: use path segment, header, and query param\n                        .withBody(&quot;&quot;&quot;\n                            {\n                              &quot;id&quot;: &quot;{{request.pathSegments.[1]}}&quot;,\n                              &quot;correlationId&quot;: &quot;{{request.headers.X-Correlation-Id}}&quot;,\n                              &quot;greeting&quot;: &quot;Hello, {{request.query.name}}&quot;\n                            }\n                            &quot;&quot;&quot;)\n                        \/\/ Ensure the response goes through the template engine:\n                        .withTransformers(&quot;response-template&quot;)\n                )\n        );\n\n        \/\/ Simple validation (manual): curl the endpoint:\n        \/\/ curl -H &quot;X-Correlation-Id: abc-123&quot; &quot;http:\/\/localhost:8080\/customers\/42?name=Alice&quot;\n        \/\/ Response should contain id=42, correlationId=abc-123, greeting &quot;Hello, Alice&quot;.\n    }\n}<\/code><\/pre>\n<p>Why this shape:<\/p>\n<ul>\n<li>We keep the <strong>logic in the template<\/strong>, not in Java branching, so QA or other devs can read and modify it like a contract.<\/li>\n<li>The same body can be copied verbatim into a JSON mapping file if you later move to standalone\/Cloud, which makes your tests more portable.<\/li>\n<\/ul>\n<hr \/>\n<h2>Handlebars helpers: beyond simple interpolation<\/h2>\n<p>Handlebars in WireMock is extended with a large set of helpers for dates, random values, JSON\/XML processing, math, array manipulation, and more. This lets templates stay expressive without turning into full programming languages.<\/p>\n<p>Some particularly pragmatic helpers:<\/p>\n<ul>\n<li><strong>Date\/time<\/strong>: <code>now<\/code> with format, timezone, and offsets; <code>parseDate<\/code>, <code>truncateDate<\/code> for consistent timestamps.<\/li>\n<li><strong>Random<\/strong>: <code>randomValue<\/code>, <code>randomInt<\/code>, <code>pickRandom<\/code> for synthetic but realistic data.<\/li>\n<li><strong>JSON<\/strong>: <code>jsonPath<\/code>, <code>parseJson<\/code>, <code>toJson<\/code>, <code>formatJson<\/code>, <code>jsonArrayAdd<\/code>, <code>jsonMerge<\/code>, <code>jsonRemove<\/code>, <code>jsonSort<\/code> for shaping JSON payloads.<\/li>\n<li><strong>XPath \/ XML<\/strong>: <code>xPath<\/code>, <code>soapXPath<\/code>, <code>formatXml<\/code>.<\/li>\n<li><strong>Utility<\/strong>: <code>math<\/code>, <code>range<\/code>, <code>contains<\/code>, <code>matches<\/code>, <code>numberFormat<\/code>, <code>base64<\/code>, <code>urlEncode<\/code>, <code>formData<\/code>, <code>regexExtract<\/code>, <code>size<\/code>, and more.<\/li>\n<\/ul>\n<p>Rationale: you can keep test data generation in the template layer, avoiding brittle fixture code and making mocks self-describing.<\/p>\n<hr \/>\n<h2>Response builders: the imperative layer<\/h2>\n<p>While Handlebars owns the declarative side, the Java <strong>Response builder<\/strong> APIs give you full programmatic control. Conceptually, you work with:<\/p>\n<ul>\n<li><code>ResponseDefinitionBuilder<\/code> (via <code>aResponse()<\/code> in stubs) at the configuration level.<\/li>\n<li><code>Response<\/code> and its <code>Builder<\/code> when writing extensions like <code>ResponseTransformerV2<\/code>.<\/li>\n<\/ul>\n<p>A typical stub already uses the builder implicitly:<\/p>\n<pre><code class=\"language-java\">wm.stubFor(\n    get(urlEqualTo(&quot;\/hello&quot;))\n        .willReturn(\n            aResponse()\n                .withStatus(200)\n                .withHeader(&quot;Content-Type&quot;, &quot;text\/plain&quot;)\n                .withBody(&quot;Hello from WireMock&quot;)\n        )\n);<\/code><\/pre>\n<p>Here the builder is used in its <strong>simplest<\/strong> form: static status, headers, and body. The real power shows up when you combine it with extensions.<\/p>\n<h2>Custom transformer with Response.Builder<\/h2>\n<p>A common pattern is: start with the stub\u2019s definition, then refine the actual <code>Response<\/code> in a transformer. This is where <code>Response.Builder<\/code> (or the <code>like(response).but()<\/code> style) becomes useful.<\/p>\n<p>Conceptual example using a <code>ResponseTransformerV2<\/code>, which takes the already-resolved <code>Response<\/code> and lets you mutate it:<\/p>\n<pre><code class=\"language-java\">import com.github.tomakehurst.wiremock.extension.ResponseTransformerV2;\nimport com.github.tomakehurst.wiremock.http.Request;\nimport com.github.tomakehurst.wiremock.http.Response;\nimport com.github.tomakehurst.wiremock.stubbing.ServeEvent;\n\npublic class UppercaseNameTransformer implements ResponseTransformerV2 {\n\n    @Override\n    public String getName() {\n        return &quot;uppercase-name-transformer&quot;;\n    }\n\n    @Override\n    public Response transform(Response response, ServeEvent serveEvent) {\n        String originalBody = response.getBodyAsString();\n        Request request = serveEvent.getRequest();\n        String name = request.queryParameter(&quot;name&quot;).isPresent()\n                ? request.queryParameter(&quot;name&quot;).firstValue()\n                : null;\n\n        String effectiveName = (name != null &amp;&amp; !name.isBlank())\n                ? name.toUpperCase()\n                : &quot;UNKNOWN&quot;;\n\n        String transformedBody = originalBody.replace(&quot;{{NAME}}&quot;, effectiveName);\n\n        return Response.Builder\n                .like(response)\n                .but()\n                .body(transformedBody)\n                .build();\n    }\n}<\/code><\/pre>\n<p>Key rationale:<\/p>\n<ul>\n<li><strong>Immutability and reuse<\/strong>: <code>like(response).but()<\/code> copies status, headers, etc., so you only specify what changes.<\/li>\n<li><strong>Composability<\/strong>: multiple transformers can run without trampling each other; each focuses on a narrow concern.<\/li>\n<li><strong>Testability<\/strong>: behaviour is in Java, which you can unit test thoroughly when it gets complex.<\/li>\n<\/ul>\n<p>A stub can then provide the template \u201cskeleton\u201d that this transformer fills:<\/p>\n<pre><code class=\"language-java\">wm.stubFor(\n    get(urlPathEqualTo(&quot;\/greet&quot;))\n        .willReturn(\n            aResponse()\n                .withStatus(200)\n                .withHeader(&quot;Content-Type&quot;, &quot;text\/plain&quot;)\n                .withBody(&quot;Hello, {{NAME}}!&quot;) \/\/ Placeholder, not Handlebars here\n                .withTransformers(&quot;uppercase-name-transformer&quot;)\n        )\n);<\/code><\/pre>\n<p>Validation strategy:<\/p>\n<ul>\n<li>Hit <code>\/greet?name=alice<\/code> and assert you get <code>Hello, ALICE!<\/code>.<\/li>\n<li>Hit <code>\/greet<\/code> with no <code>name<\/code> and assert you get <code>Hello, UNKNOWN!<\/code>.<\/li>\n<\/ul>\n<p>This demonstrates the <strong>division of responsibilities<\/strong>: the stub defines the <em>shape<\/em>, the transformer plus builder define the <em>behaviour<\/em>.<\/p>\n<hr \/>\n<h2>Example: combining Handlebars and Response builders<\/h2>\n<p>To tie everything together, here is a minimal Java program that:<\/p>\n<ul>\n<li>Starts WireMock.<\/li>\n<li>Defines one stub using pure Handlebars.<\/li>\n<li>Defines another stub that uses a custom transformer implemented with <code>Response.Builder<\/code>.<\/li>\n<\/ul>\n<p>Again, no build tooling, only the code that matters for behaviour.<\/p>\n<pre><code class=\"language-java\">import com.github.tomakehurst.wiremock.WireMockServer;\nimport com.github.tomakehurst.wiremock.core.WireMockConfiguration;\nimport com.github.tomakehurst.wiremock.extension.ResponseTransformerV2;\nimport com.github.tomakehurst.wiremock.http.Request;\nimport com.github.tomakehurst.wiremock.http.Response;\nimport com.github.tomakehurst.wiremock.stubbing.ServeEvent;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\n\npublic class WireMockTemplatingDemo {\n\n    public static void main(String[] args) {\n        WireMockServer wm = new WireMockServer(\n                WireMockConfiguration.options()\n                        .port(8080)\n                        .templatingEnabled(true) \/\/ ensure Handlebars engine is available\n                        .extensions(new UppercaseNameTransformer())\n        );\n        wm.start();\n\n        configureFor(&quot;localhost&quot;, 8080);\n\n        \/\/ 1) Pure Handlebars-based response.\n        wm.stubFor(\n                get(urlPathMatching(&quot;\/customers\/(.*)&quot;))\n                        .willReturn(\n                                aResponse()\n                                        .withStatus(200)\n                                        .withHeader(&quot;Content-Type&quot;, &quot;application\/json&quot;)\n                                        .withBody(&quot;&quot;&quot;\n                            {\n                              &quot;id&quot;: &quot;{{request.pathSegments.[1]}}&quot;,\n                              &quot;name&quot;: &quot;{{request.query.name}}&quot;,\n                              &quot;requestId&quot;: &quot;{{request.headers.X-Request-Id}}&quot;\n                            }\n                            &quot;&quot;&quot;)\n                                        .withTransformers(&quot;response-template&quot;)\n                        )\n        );\n\n        \/\/ 2) Response builder + custom transformer.\n        wm.stubFor(\n                get(urlPathEqualTo(&quot;\/welcome&quot;))\n                        .willReturn(\n                                aResponse()\n                                        .withStatus(200)\n                                        .withHeader(&quot;Content-Type&quot;, &quot;text\/plain&quot;)\n                                        \/\/ Placeholder token; the transformer will replace it.\n                                        .withBody(&quot;Welcome, {{NAME}}!&quot;)\n                                        .withTransformers(&quot;uppercase-name-transformer&quot;)\n                        )\n        );\n\n        System.out.println(&quot;WireMock started on http:\/\/localhost:8080&quot;);\n\n        \/\/ Manual validation:\n        \/\/ 1) Handlebars endpoint:\n        \/\/ curl -H &quot;X-Request-Id: req-123&quot; &quot;http:\/\/localhost:8080\/customers\/42?name=Alice&quot;\n        \/\/    -&gt; JSON with id=42, name=Alice, requestId=req-123\n        \/\/\n        \/\/ 2) Transformer + Response.Builder endpoint:\n        \/\/ curl &quot;http:\/\/localhost:8080\/welcome?name=alice&quot;\n        \/\/    -&gt; &quot;Welcome, ALICE!&quot;\n        \/\/ curl &quot;http:\/\/localhost:8080\/welcome&quot;\n        \/\/    -&gt; &quot;Welcome, UNKNOWN!&quot;\n    }\n\n    \/\/ Custom transformer using Response.Builder\n    public static class UppercaseNameTransformer implements ResponseTransformerV2 {\n\n        @Override\n        public String getName() {\n            return &quot;uppercase-name-transformer&quot;;\n        }\n\n        @Override\n        public Response transform(Response response, ServeEvent serveEvent) {\n            String body = response.getBodyAsString();\n            Request request = serveEvent.getRequest();\n            String name = request.queryParameter(&quot;name&quot;).isPresent()\n                    ? request.queryParameter(&quot;name&quot;).firstValue()\n                    : null;\n\n            String effectiveName = (name != null &amp;&amp; !name.isBlank())\n                    ? name.toUpperCase()\n                    : &quot;UNKNOWN&quot;;\n\n            String newBody = body.replace(&quot;{{NAME}}&quot;, effectiveName);\n\n            return Response.Builder\n                    .like(response)\n                    .but()\n                    .body(newBody)\n                    .build();\n        }\n    }\n}<\/code><\/pre>\n<p>Why this example is structured this way:<\/p>\n<ul>\n<li>It demonstrates that <strong>Handlebars and Response builders are orthogonal<\/strong> tools: you can use either alone, or both together.<\/li>\n<li>The Handlebars stub stays close to an API contract and is easy to lift into JSON mappings later.<\/li>\n<li>The transformer shows how to use <strong>Response.Builder for behavioural logic<\/strong> while preserving the stub\u2019s declarative shape.<\/li>\n<\/ul>\n<hr \/>\n<h2>When to prefer which approach<\/h2>\n<p>In practice, a good rule of thumb is:<\/p>\n<ul>\n<li>Start with <strong>Handlebars templates<\/strong> whenever the response can be expressed as \u201crequest data + light helper usage\u201d. This keeps mocks transparent and editable by a wide audience.<\/li>\n<li>Move logic into <strong>Response builders and transformers<\/strong> when you need non-trivial algorithms, external data sources, or complex JSON manipulation that would make templates hard to read.<\/li>\n<\/ul>\n<p>A lot of teams settle on a hybrid: templates for the bulk of the response, plus a few narrow custom transformers for cross-cutting concerns (IDs, timestamps, test data injection). That balance usually gives you both readability and power.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>WireMock gives you two powerful levers for dynamic responses: declarative Handlebars templates embedded in stubs, and imperative Java logic via the response builder APIs. They complement each other rather than compete. Why response templating exists In real systems, responses rarely stay static: IDs echo from the URL, correlation IDs flow through headers, timestamps change, payloads [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[76,96],"tags":[],"_links":{"self":[{"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/posts\/2164"}],"collection":[{"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=2164"}],"version-history":[{"count":1,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/posts\/2164\/revisions"}],"predecessor-version":[{"id":2166,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=\/wp\/v2\/posts\/2164\/revisions\/2166"}],"wp:attachment":[{"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=2164"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=2164"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.ronella.xyz\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=2164"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}