Request and Response Transformation
Strip, rename, inject, and redact fields in a request or response — without changing the backend. This recipe covers three common transformation patterns: injecting headers before forwarding, reshaping the response body, and serving different response shapes per partner.
Typical scenario: Your backend API returns a verbose payload with internal field names. You need to expose a clean, versioned schema to partners — renaming fields, removing internal data, and injecting correlation headers — without changing the backend service.
Prerequisites
- You are signed in to the Management UI as an admin or editor
- A collection and proxy exist (e.g.
orders-api,GET /orders/{orderId}) - An upstream service is reachable (e.g.
https://orders.internal/v3/orders/{orderId})
Pattern A — Inject request headers before forwarding
Inject a correlation header and an internal service token before the request reaches the upstream. The client does not need to know about these headers.
Workflow
Steps
- Open the proxy workflow canvas.
- Add a Set node after
http_trigger. Name itinject-headers. - Configure the output to build the enriched header map:
{
"X-Internal-Token": "{{env.INTERNAL_SERVICE_TOKEN}}",
"X-Correlation-ID": "{{trigger.headers['X-Request-ID'] || uuid()}}",
"X-Client-ID": "{{trigger.headers['X-Client-ID']}}",
"Authorization": "{{trigger.headers['Authorization']}}"
}
- Add an
http_request_node, connect frominject-headers. - Configure:
- URL:
https://orders.internal/v3/orders/{{trigger.params.orderId}} - Headers: use
{{inject-headers.output}}as the headers object
- URL:
- Connect a Response node to pass through
{{upstream.response.body}}with status{{upstream.response.status}}.
Pattern B — Reshape the response body
The upstream returns an internal schema. Transform it to a clean public schema before returning to the client.
Upstream response (internal schema):
{
"ord_id": "ORD-9182",
"cust_ref": "C-4421",
"line_items": [{ "sku": "P-001", "qty": 2, "unit_price_cents": 4999 }],
"ts_created": 1741526400,
"internal_region_code": "us-west-2a",
"fulfillment_partner_id": "FP-77"
}
Target public schema:
{
"orderId": "ORD-9182",
"customerId": "C-4421",
"items": [{ "sku": "P-001", "quantity": 2, "unitPrice": 49.99 }],
"createdAt": "2026-03-09T14:00:00Z",
"region": "us-west"
}
Steps
- After the
http_request_node, add a Code node. Name ittransform-response. - Write the transformation:
const raw = input.upstream.response.body;
return {
orderId: raw.ord_id,
customerId: raw.cust_ref,
items: (raw.line_items || []).map(item => ({
sku: item.sku,
quantity: item.qty,
unitPrice: item.unit_price_cents / 100
})),
createdAt: new Date(raw.ts_created * 1000).toISOString(),
region: raw.internal_region_code?.split('-').slice(0, 2).join('-') ?? null
// internal_region_code and fulfillment_partner_id are intentionally excluded
};
- Connect a Response node:
- Status:
200 - Body:
{{transform-response.output}} - Content-Type:
application/json
- Status:
Fields internal_region_code and fulfillment_partner_id are dropped automatically by not including them in the output object. No explicit delete is required.
Pattern C — Serve different schemas per partner
Partner A expects the v1 schema. Partner B expects v2. Use a condition node to branch on the incoming X-Profile-ID or X-API-Version header and apply the correct transformation.
Workflow
Steps
- After the
http_request_node, add a Condition node. Name itcheck-version. - Expression:
{{trigger.headers['X-API-Version']}} == "v2"
True branch (v2 transform) — Set node:
{
"orderId": "{{upstream.response.body.ord_id}}",
"customerId": "{{upstream.response.body.cust_ref}}",
"lineItems": "{{upstream.response.body.line_items}}",
"createdAt": "{{upstream.response.body.ts_created | toISO}}"
}
False branch (v1 transform) — Set node:
{
"order_id": "{{upstream.response.body.ord_id}}",
"customer_ref": "{{upstream.response.body.cust_ref}}",
"items": "{{upstream.response.body.line_items}}"
}
- Connect both branches to a single Response node.
Verify
Pattern A — Header injection:
curl -i https://gateway.example.com/orders-api/orders/ORD-9182 \
-H "X-Client-ID: <client-id>" \
-H "Authorization: Bearer <token>" \
-H "X-Profile-ID: <profile-id>" \
-H "X-Request-ID: req-abc-123"
Confirm on your upstream service logs that X-Internal-Token and X-Correlation-ID were received.
Pattern B — Body transformation:
Confirm the response body contains orderId (not ord_id) and that internal_region_code is absent:
curl https://gateway.example.com/orders-api/orders/ORD-9182 ... | jq 'keys'
# Expected: ["createdAt","customerId","items","orderId","region"]
# Not present: internal_region_code, fulfillment_partner_id
Pattern C — Version-based schema:
# v2 schema
curl ... -H "X-API-Version: v2" | jq 'keys'
# Expected: ["createdAt","customerId","lineItems","orderId"]
# v1 schema (no version header)
curl ... | jq 'keys'
# Expected: ["customer_ref","items","order_id"]
Common transformation patterns
| Goal | Approach |
|---|---|
| Rename a field | set_node — map old key to new key |
| Drop sensitive fields | code_node — return object without the field |
| Convert epoch to ISO 8601 | code_node — new Date(ts * 1000).toISOString() |
| Convert cents to decimal | code_node — price_cents / 100 |
| Inject upstream auth header | set_node — map env.TOKEN into headers |
| Version-based schema switch | condition_node + two set_node branches |
| Wrap response in envelope | set_node — { data: ..., meta: { ... } } |