Replay¶
Record API responses and replay them on subsequent requests that match specific request fields. Works like VCR - record once, replay on match.
cache:
replay:
ttl: 24h
auto-replay: false
upstream-only: false
endpoints:
/foo/{f-id}/bar/{b-id}:
POST:
match:
path:
- f-id
body:
- data.name
- data.address.zip
How It Works¶
- A request comes in with the
X-Cxs-Replayheader (or to anauto-replayendpoint) - Specified fields are extracted from the request body and/or query string
- A content-addressed key is built from the method, path pattern, and extracted values
- If a recording exists for that key - return it immediately with
X-Cxs-Source: replay - If no recording exists - forward to downstream, capture the response, store it, and return it
Activation¶
Replay activates in two ways:
Header-based (default): Send the X-Cxs-Replay header to activate replay for any request.
# Empty header - uses match fields from config
curl -X POST /svc/foo/123/bar/456 \
-H "X-Cxs-Replay:" \
-d '{"data": {"name": "Jane", "address": {"zip": "12345"}}}'
# Header with body fields - overrides config
curl -X POST /svc/foo/123/bar/456 \
-H "X-Cxs-Replay: data.name,data.address.zip" \
-d '{"data": {"name": "Jane", "address": {"zip": "12345"}}}'
# Header with explicit body and query fields
curl -X POST /svc/pay?channel=web \
-H "X-Cxs-Replay: body:biller,reference;query:channel" \
-d 'biller=BLR0001&reference=REF123'
Fields can also include path variables using the path: prefix:
# Header with path, body, and query fields
curl -X POST /svc/pay/credit-card/tx/123 \
-H "X-Cxs-Replay: path:paymentMethodName;body:reference;query:channel" \
-d 'reference=REF123'
Unqualified fields in the header (without path:, body:, or query: prefix) are treated as body fields.
Auto-replay: Set auto-replay: true in config to activate for configured endpoints without requiring the header.
cache:
replay:
auto-replay: true
endpoints:
/users:
POST:
match:
body:
- email
Endpoint Configuration¶
The HTTP method level is optional. Three forms are supported:
endpoints:
# Path only - matches any HTTP method, no match fields
/health:
# Path + HTTP method - matches only POST, no match fields (key is HTTP method + path only)
/notify:
POST:
# Path + HTTP method + match - full config
/search:
POST:
match:
body:
- term
- filters.category
When the HTTP method is omitted, the request's actual HTTP method is used for key building.
When multiple HTTP methods are configured for the same path, the request method must match exactly:
endpoints:
/users/{user-id}/orders:
POST:
match:
body:
- items[0].product_id
- shipping.method
PUT:
match:
body:
- order_id
Match Fields¶
Match fields specify which request values form the replay key. Each field is explicitly sourced from path variables, the request body, or the query string.
match:
path: # extracted from URL path variables
- paymentMethodName
body: # extracted from request body
- biller
- reference
query: # extracted from URL query string
- channel
Path fields¶
Path fields include specific path variable values in the replay key. By default, path variables are ignored - all path variable values produce the same key (e.g., /pay/credit-card and /pay/bank-transfer share recordings). Adding path variables to the match config makes them produce different keys.
endpoints:
/pay/{paymentMethodName}/tx/{txId}:
POST:
match:
path:
- paymentMethodName # include in key - different payment methods get different recordings
body: # txId is NOT listed - all txIds share recordings
- reference
# credit-card and bank-transfer produce different recordings
curl -X POST /svc/pay/credit-card/tx/123 \
-H "X-Cxs-Replay:" \
-d 'reference=REF123'
curl -X POST /svc/pay/bank-transfer/tx/456 \
-H "X-Cxs-Replay:" \
-d 'reference=REF123'
Body fields¶
Body fields are extracted based on the request's Content-Type:
JSON body - dotted paths for nested structures:
match:
body:
- data.name # simple nested path
- data.items[0].name # array index
- data.items.name # array wildcard (first match)
- "[0].name" # top-level array index
curl -X POST /svc/search \
-H "Content-Type: application/json" \
-H "X-Cxs-Replay:" \
-d '{"data": {"name": "Jane"}}'
JSON array body - when the root is an array, use [index] or wildcard:
match:
body:
- "[0].name" # first element's name
- name # wildcard - searches all elements, returns first match
curl -X POST /svc/pets \
-H "Content-Type: application/json" \
-H "X-Cxs-Replay:" \
-d '[{"name": "doggie", "tag": "fundamental-window"}]'
Form-encoded body - flat keys matching form field names:
match:
body:
- biller
- reference
curl -X POST /svc/pay \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "X-Cxs-Replay:" \
-d 'amount=50&biller=BLR0001&reference=REF123'
Query fields¶
Query fields are extracted from the URL query string:
match:
query:
- amount
- biller
curl /svc/pay?amount=50&biller=BLR0001 \
-H "X-Cxs-Replay:"
Mixed: body + query¶
Body and query fields can be combined:
match:
body:
- biller
- reference
query:
- channel
curl -X POST /svc/pay?channel=web \
-H "Content-Type: application/x-www-form-urlencoded" \
-H "X-Cxs-Replay:" \
-d 'amount=50&biller=BLR0001&reference=REF123'
Here biller and reference come from the form body, channel comes from the query string.
Key Design¶
The replay key is a SHA-256 hash of: METHOD:pattern_path|body:field1=value1|path:var1=value1|query:field2=value2|...
- Pattern path is used (e.g.,
/foo/{id}/bar), not the actual URL - so different path parameter values share recordings unless explicitly included viapathmatch fields - Fields are sorted alphabetically for determinism
- Each field is prefixed with its source (
path:,body:, orquery:) in the key - Only the matched fields matter - other body/query/path content is ignored
- If any configured match field is missing from the request (path variable not found, body field not found, query parameter absent), replay is skipped entirely - no recording or matching is attempted
Configuration Options¶
| Option | Type | Default | Description |
|---|---|---|---|
ttl |
duration | 24h |
How long recordings are kept |
upstream-only |
bool | false |
Only record responses from upstream services |
auto-replay |
bool | false |
Activate for configured endpoints without the header |
endpoints |
map | - | Path patterns with optional methods and match fields |
Upstream-Only Mode¶
When upstream-only: true, only responses from upstream services are recorded. If the response is not from upstream (e.g., generated or cached), the middleware returns a 502 Bad Gateway error instead of passing the response through. This makes it explicit to the caller that the recording was skipped.
cache:
replay:
upstream-only: true
endpoints:
/external-api/search:
POST:
match:
body:
- term
- filters.category
Example: Full Configuration¶
name: my-service
upstream:
url: https://api.example.com
cache:
requests: true
replay:
ttl: 12h
upstream-only: true
auto-replay: false
endpoints:
# JSON body match
/search:
POST:
match:
body:
- term
- filters.category
# Multiple methods with different match fields
/users/{user-id}/orders:
POST:
match:
body:
- items[0].product_id
- shipping.method
PUT:
match:
body:
- order_id
# Path variable + body match
/pay/{paymentMethodName}:
POST:
match:
path:
- paymentMethodName
body:
- biller
- reference
query:
- channel
# Query string only
/lookup:
GET:
match:
query:
- account_id
- type
# Path only - match by method + path, any HTTP method
/health: