What “Transformation” Means at the Gateway
Request and response transformation is the set of gateway policies that modify what a client sends and what an upstream service receives (and vice versa) without requiring changes to the upstream code. This is useful when you need to expose a stable public API contract while upstream services evolve, or when different clients require different shapes of data.
Transformations typically happen in three places: (1) request metadata (headers, query string, path), (2) request/response bodies (payload mapping and content-type conversion), and (3) response shaping (filtering fields, adding metadata, normalizing errors). Because transformations can add CPU work and complexity, apply them deliberately and keep them observable (logs/metrics) so you can detect drift and performance impact.
Header Manipulation (Add, Remove, Rename)
Headers are the easiest and most common transformation surface. They carry identity, correlation, content negotiation, caching hints, and client context. A gateway can add, remove, or rename headers to match an upstream contract or enforce consistent behavior.
Add headers
- Correlation IDs: If a client does not send a request ID, the gateway can generate one and forward it upstream (and echo it back in the response) to enable tracing.
- Client context: Add
X-Client-App,X-Client-Version, or a derived header likeX-Geo-Country(from IP intelligence) if your upstream expects it. - Forwarded headers: Normalize
Forwarded/X-Forwarded-For/X-Forwarded-Protoso upstream services can build correct absolute URLs.
Remove headers
- Security: Strip headers that should never reach upstreams (for example, internal-only headers like
X-Internal-User). - Prevent spoofing: Remove client-supplied
X-Forwarded-Forand replace it with the gateway’s computed value to avoid IP spoofing. - Reduce ambiguity: Remove duplicate or conflicting headers (for example, multiple
Content-Typevalues).
Rename headers
Renaming is useful when you are migrating contracts. Example: clients send X-Request-Id but upstream expects X-Correlation-Id. The gateway can map one to the other.
# Conceptual mapping (policy-style pseudocode) add/rename/remove headers: if missing X-Request-Id: set X-Request-Id = generated_uuid() set X-Correlation-Id = header("X-Request-Id") remove header("X-Deprecated-Header")Cautions for header transformations
- Hop-by-hop headers: Be careful with headers like
Connection,Keep-Alive,Transfer-Encoding, andUpgrade. Many gateways manage these at the proxy layer; rewriting them incorrectly can break connections. - Caching behavior: Changing
Vary,Cache-Control, orETagcan change cache correctness. If you shape responses, ensure caching headers still match the actual payload. - Case and duplicates: HTTP header names are case-insensitive, but some libraries behave oddly with duplicates. Prefer a single canonical header name and a single value when possible.
Query String Mapping and Path Rewrites
When clients call a public API, they often use friendly query parameters and paths. Upstream services may use different names, formats, or resource layouts. Gateways can translate between these contracts.
Continue in our app.
You can listen to the audiobook with the screen off, receive a free certificate for this course, and also have access to 5,000 other free online courses.
Or continue reading below...Download the app
Query string mapping
Common patterns include renaming parameters, changing formats, and applying defaults.
- Rename:
?pageSize=50to?limit=50 - Format conversion:
?from=2026-01-01to?start_date=1704067200(epoch seconds) - Defaults: If
sortis missing, setsort=created_at:desc - Allow-listing: Drop unknown query parameters to reduce accidental coupling and prevent upstream surprises.
# Example mapping (conceptual) incoming: GET /v1/orders?pageSize=50&from=2026-01-01 outgoing to upstream: GET /orders?limit=50&start_date=1704067200Path rewrites
Path rewrites let you expose stable, versioned, or more user-friendly URLs while routing to different upstream resource layouts.
- Version facade: Public
/v1/customers/{id}to upstream/customers/{id} - Resource rename: Public
/usersto upstream/accounts - Composite paths: Public
/stores/{storeId}/ordersto upstream/orders?store_id={storeId}
# Example rewrite (conceptual) incoming: GET /v1/stores/42/orders outgoing: GET /orders?store_id=42Cautions for query/path transformations
- Coupling risk: The more your gateway “knows” about upstream parameter semantics, the more changes upstream can force on gateway policies. Keep mappings small and well-documented.
- Observability: Log both the external request and the transformed upstream request (carefully redacting sensitive values) to debug mismatches.
- Edge cases: Watch for URL encoding, repeated parameters (e.g.,
tag=a&tag=b), and trailing slashes.
Body Transformations (JSON Field Mapping)
Body transformations modify the payload itself. The most common case is JSON field mapping: renaming fields, changing nesting, converting types, and applying defaults. This is powerful but can be expensive and can create tight coupling between the gateway and both schemas.
Typical JSON mapping operations
- Rename fields:
customerIdtocustomer_id - Move fields:
addressLine1intoaddress.line1 - Type conversion:
"true"(string) totrue(boolean), or numeric strings to numbers - Defaults: If
currencymissing, setcurrency="USD" - Drop fields: Remove client-only fields not accepted upstream
# External request body (client contract) { "customerId": "123", "addressLine1": "10 Main St", "newsletter": "true" } # Upstream request body (upstream contract) { "customer_id": 123, "address": { "line1": "10 Main St" }, "newsletter": true }Validation vs transformation
Validation checks whether the input matches expectations; transformation changes it to match an upstream contract. In practice, you usually do both: validate the external contract first (to give the client a clear error), then transform to the upstream contract.
- Validate early: Reject missing required fields, wrong types, and invalid formats before you spend time transforming.
- Transform deterministically: Avoid “best effort” conversions that hide client mistakes (for example, silently turning invalid dates into null).
Cautions for body transformations
- Latency and CPU: Parsing and rewriting JSON adds overhead. Large payloads amplify this cost.
- Streaming limitations: Some transformations require buffering the entire body, which can increase memory use and reduce throughput.
- Schema drift: If upstream adds/removes fields, your mapping can break. Treat mappings as code: version them, test them, and deploy them with change control.
Content-Type Conversion Basics
Sometimes clients and upstreams speak different formats. A gateway can perform basic content-type conversion, most commonly between JSON and form-encoded payloads, or between JSON and XML in legacy environments.
Common conversions
- JSON to
application/x-www-form-urlencoded: Useful when upstream expects classic form posts. - XML to JSON: Useful when exposing a modern API facade over a legacy XML service.
- Content negotiation: Use
AcceptandContent-Typeto decide what to produce/consume, but keep the supported set small.
# Example: JSON to form-encoded (conceptual) incoming Content-Type: application/json body: { "grant_type": "client_credentials", "scope": "read" } outgoing Content-Type: application/x-www-form-urlencoded body: grant_type=client_credentials&scope=readCautions for content-type conversion
- Lossy mappings: XML attributes, mixed content, and repeated elements can map ambiguously to JSON. Define explicit rules and test with real payloads.
- Character encoding: Ensure consistent UTF-8 handling and correct
charsetparameters when relevant. - Validation first: Validate the incoming payload before converting; otherwise you may generate confusing upstream errors.
Response Shaping (Filtering Fields, Adding Metadata)
Response shaping modifies what clients receive without changing what upstream returns. This is useful for hiding internal fields, reducing payload size for mobile clients, or adding gateway-generated metadata.
Filtering and projection
- Remove sensitive fields: Strip internal IDs, debug fields, or fields that should not be public.
- Reduce payload size: Return only a subset of fields needed by a given endpoint.
- Consistent naming: Convert upstream
snake_caseto externalcamelCase(or the reverse) if your public contract requires it.
# Upstream response { "id": 10, "email": "a@example.com", "internal_notes": "VIP", "created_at": "2026-01-16T10:00:00Z" } # External response after shaping { "id": 10, "email": "a@example.com", "createdAt": "2026-01-16T10:00:00Z" }Adding metadata
Gateways can add response headers or body fields that help clients operate reliably.
- Echo correlation ID: Return
X-Request-Idso clients can reference it in support tickets. - Pagination metadata: Add
nextPageTokenorlinksif the upstream uses a different pagination style. - Deprecation signals: Add headers like
DeprecationorSunsetwhen you need to communicate lifecycle changes (ensure they match your API governance).
Cautions for response shaping
- Caching correctness: If you shape responses differently per client (e.g., based on headers), ensure
Varyreflects that or disable shared caching. - Error transparency: If you rewrite error bodies, preserve enough detail for clients while avoiding leakage of internal stack traces or identifiers.
- Latency: Shaping requires parsing and rewriting; measure the added time and consider limiting shaping to endpoints where it provides clear value.
CORS Handling and Preflight Requests
Cross-Origin Resource Sharing (CORS) is a browser security mechanism that controls whether a web page can call an API from a different origin. Gateways commonly centralize CORS so upstream services do not need to implement it consistently.
Key headers
Access-Control-Allow-Origin: Which origins are allowed (avoid using*when credentials are involved).Access-Control-Allow-Methods: Allowed HTTP methods.Access-Control-Allow-Headers: Allowed request headers (especially custom headers).Access-Control-Allow-Credentials: Whether cookies/credentials are allowed (requires a specific origin, not*).Access-Control-Expose-Headers: Which response headers the browser can read.Access-Control-Max-Age: How long the browser can cache preflight results.
Preflight (OPTIONS) requests
For “non-simple” requests (e.g., custom headers or methods like PUT), browsers send an OPTIONS preflight request before the actual call. A gateway can respond to preflight directly without forwarding to upstream, reducing load and avoiding inconsistent upstream behavior.
# Preflight request (browser) OPTIONS /v1/orders Origin: https://app.example.com Access-Control-Request-Method: POST Access-Control-Request-Headers: content-type,x-request-id # Preflight response (gateway) 204 No Content Access-Control-Allow-Origin: https://app.example.com Access-Control-Allow-Methods: GET,POST,PUT,DELETE,OPTIONS Access-Control-Allow-Headers: content-type,x-request-id Access-Control-Max-Age: 600CORS cautions
- Origin reflection: If you dynamically echo
Origin, validate it against an allow-list to avoid opening your API to any site. - Credentials: If
Access-Control-Allow-Credentials: true, you must not use*forAccess-Control-Allow-Origin. - Consistency: Ensure both preflight and actual responses include the required CORS headers; otherwise browsers will still block the call.
A Practical Transformation Sequence (External Contract → Upstream Contract → External Contract)
A reliable approach is to treat the gateway as a contract adapter with a clear pipeline. The goal is predictable behavior: clients get consistent validation errors, upstreams receive exactly what they expect, and clients receive a stable external response shape.
Step 1: Validate the external input shape
- Validate headers: Require or forbid certain headers (e.g., require
Content-Type: application/jsonfor JSON endpoints; reject unexpectedContent-Encodingif unsupported). - Validate query/path: Check required parameters, allowed values, and formats (e.g.,
pageSizerange). - Validate body schema: Ensure required fields exist and types/formats are correct (e.g.,
customerIdis numeric,emailis valid).
# Example validation rules (conceptual) require header Content-Type == "application/json" require query param pageSize in [1..100] require body.customerId is integer require body.newsletter is booleanStep 2: Transform the request to the upstream contract
- Headers: Add correlation ID, rename headers, strip spoofable headers.
- Query/path: Rewrite paths and map query parameters to upstream names and formats.
- Body: Map JSON fields, convert types, apply defaults, remove unsupported fields.
- Content-type: Convert payload format if the upstream requires a different
Content-Type.
# External request (client) POST /v1/orders?pageSize=50 Header: X-Request-Id: abc-123 Body: { "customerId": "123", "items": [{"sku":"A1","qty":"2"}] } # Transformed upstream request POST /orders?limit=50 Header: X-Correlation-Id: abc-123 Body: { "customer_id": 123, "lines": [{"sku":"A1","quantity": 2}] }Step 3: Transform the upstream response to the external contract
- Status and headers: Optionally normalize headers (e.g., echo
X-Request-Id), and ensure CORS headers are present for browser clients. - Body shaping: Rename fields, filter internal fields, and add external metadata (e.g., pagination links).
- Error normalization: If you standardize error shapes, map upstream error formats into your public error contract while preserving an internal correlation ID for debugging.
# Upstream response { "order_id": 999, "customer_id": 123, "internal_state": "RISK_REVIEW" } # External response { "orderId": 999, "customerId": 123, "metadata": { "requestId": "abc-123" } }Operational cautions for the full pipeline
- Test transformations like code: Use contract tests with sample requests/responses to catch schema drift.
- Measure added latency: Track gateway processing time separately from upstream time; transformations can become the bottleneck.
- Minimize transformation scope: Prefer small, targeted mappings over large “do everything” policies that are hard to maintain.