Skip to main content

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

  1. Open the proxy workflow canvas.
  2. Add a Set node after http_trigger. Name it inject-headers.
  3. 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']}}"
}
  1. Add an http_request_node, connect from inject-headers.
  2. Configure:
    • URL: https://orders.internal/v3/orders/{{trigger.params.orderId}}
    • Headers: use {{inject-headers.output}} as the headers object
  3. 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

  1. After the http_request_node, add a Code node. Name it transform-response.
  2. 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
};
  1. Connect a Response node:
    • Status: 200
    • Body: {{transform-response.output}}
    • Content-Type: application/json
note

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

  1. After the http_request_node, add a Condition node. Name it check-version.
  2. 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}}"
}
  1. 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

GoalApproach
Rename a fieldset_node — map old key to new key
Drop sensitive fieldscode_node — return object without the field
Convert epoch to ISO 8601code_nodenew Date(ts * 1000).toISOString()
Convert cents to decimalcode_nodeprice_cents / 100
Inject upstream auth headerset_node — map env.TOKEN into headers
Version-based schema switchcondition_node + two set_node branches
Wrap response in envelopeset_node{ data: ..., meta: { ... } }

Next steps