Summary
Mock responses are currently fully static JSON blobs. Add lightweight response templating so mocks can echo back data from the incoming request — path parameters, query params, headers, or body fields — without writing custom code.
This dramatically reduces the number of mock files needed and makes mocks feel more like real APIs.
Motivation
Common use cases that are impossible today:
- Echo back the ID from the URL:
GET /users/42 → { "id": 42 }
- Return the submitted username in the response body
- Include a correlation ID from the request header in the response
- Build a
Location header value that references the created resource
Proposed Template Syntax
Use {{ }} double-brace syntax inside any string value in the response JSON:
| Template |
Source |
{{path.id}} |
Named path parameter :id |
{{query.page}} |
URL query parameter ?page=2 |
{{header.x-request-id}} |
Request header value |
{{body.username}} |
Top-level JSON body field |
{{body.user.email}} |
Nested JSON body field (dot notation) |
Example
{
"method": "POST",
"path": "/users",
"status": 201,
"response": {
"id": 99,
"username": "{{body.username}}",
"email": "{{body.email}}",
"created_by": "{{header.x-actor}}",
"self_url": "/users/99"
}
}
Request:
curl -X POST http://localhost:8080/users \
-H "X-Actor: admin" \
-H "Content-Type: application/json" \
-d '{"username":"alice","email":"alice@example.com"}'
Response:
{
"id": 99,
"username": "alice",
"email": "alice@example.com",
"created_by": "admin",
"self_url": "/users/99"
}
Implementation Plan
src/handler.rs
After matching a mock, before serializing the response, walk the serde_json::Value tree and interpolate:
fn render_template(value: &serde_json::Value, ctx: &RequestContext) -> serde_json::Value {
match value {
serde_json::Value::String(s) => {
serde_json::Value::String(interpolate(s, ctx))
}
serde_json::Value::Object(map) => {
serde_json::Value::Object(map.iter()
.map(|(k, v)| (k.clone(), render_template(v, ctx)))
.collect())
}
serde_json::Value::Array(arr) => {
serde_json::Value::Array(arr.iter().map(|v| render_template(v, ctx)).collect())
}
other => other.clone(),
}
}
interpolate replaces all {{source.key}} occurrences using a regex scan.
Dependency on Issue #4 (Path Parameters)
{{path.X}} requires path params to be captured and stored in RequestContext. The other sources (query, header, body) are already available.
Acceptance Criteria
Summary
Mock responses are currently fully static JSON blobs. Add lightweight response templating so mocks can echo back data from the incoming request — path parameters, query params, headers, or body fields — without writing custom code.
This dramatically reduces the number of mock files needed and makes mocks feel more like real APIs.
Motivation
Common use cases that are impossible today:
GET /users/42→{ "id": 42 }Locationheader value that references the created resourceProposed Template Syntax
Use
{{ }}double-brace syntax inside any string value in theresponseJSON:{{path.id}}:id{{query.page}}?page=2{{header.x-request-id}}{{body.username}}{{body.user.email}}Example
{ "method": "POST", "path": "/users", "status": 201, "response": { "id": 99, "username": "{{body.username}}", "email": "{{body.email}}", "created_by": "{{header.x-actor}}", "self_url": "/users/99" } }Request:
Response:
{ "id": 99, "username": "alice", "email": "alice@example.com", "created_by": "admin", "self_url": "/users/99" }Implementation Plan
src/handler.rsAfter matching a mock, before serializing the response, walk the
serde_json::Valuetree and interpolate:interpolatereplaces all{{source.key}}occurrences using a regex scan.Dependency on Issue #4 (Path Parameters)
{{path.X}}requires path params to be captured and stored inRequestContext. The other sources (query, header, body) are already available.Acceptance Criteria
{{query.X}},{{header.X}},{{body.X}}interpolation working{{path.X}}working (depends on path param feature){{body.a.b.c}}dot-notation for JSON body fields