Transforms Overview
Transforms in ApiCharge allow you to modify requests and responses as they flow through the proxy. This enables adaptation to backend service requirements, URL rewriting, header manipulation, and more.
Transform Categories
Transforms fall into several categories:
- Path Transforms - Modify the request path
- Header Transforms - Add, modify, or remove headers
- Query Transforms - Manipulate query parameters
- Response Transforms - Modify response headers and trailers
- Security Transforms - Handle authentication and forwarding information
Basic Transform Configuration
Transforms are configured in the Transforms
array of a route:
{
"Routes": {
"api-route": {
"ClusterId": "backend-cluster",
"Match": {
"Path": "/api/{**remainder}"
},
"Transforms": [
{ "PathPrefix": "/v2" },
{ "RequestHeader": "X-Forwarded-Service", "Set": "api-gateway" },
{ "ResponseHeader": "X-Powered-By", "Set": "ApiCharge" }
]
}
}
}
Default Transforms
ApiCharge enables several transforms by default for all routes:
- Host - Suppresses the incoming Host header
- X-Forwarded-For - Sets the client's IP address
- X-Forwarded-Proto - Sets the original scheme (http/https)
- X-Forwarded-Host - Sets the original Host header
- X-Forwarded-Prefix - Sets the original PathBase
Path Transforms
Path transforms modify the request path before forwarding to backend services. These are essential for API versioning, service routing, and URL restructuring.
PathPrefix
Adds a prefix to the beginning of the request path:
{
"Transforms": [
{ "PathPrefix": "/api/v2" }
]
}
Example: /users/123
becomes /api/v2/users/123
PathRemovePrefix
Removes a matching prefix from the request path:
{
"Transforms": [
{ "PathRemovePrefix": "/api/v1" }
]
}
Example: /api/v1/users/123
becomes /users/123
PathSet
Replaces the entire request path with a specified value:
{
"Transforms": [
{ "PathSet": "/health" }
]
}
Example: Any incoming path becomes /health
PathPattern
Uses a pattern template to rebuild the path with route values:
{
"Match": {
"Path": "/api/{service}/{version}/{**remainder}"
},
"Transforms": [
{ "PathPattern": "/{version}/api/{service}/{**remainder}" }
]
}
Example: /api/users/v2/profiles
becomes /v2/api/users/profiles
Common Path Transform Scenarios
API Versioning
{
"v1-to-v2": {
"ClusterId": "api-v2-cluster",
"Match": {
"Path": "/api/v1/{**remainder}"
},
"Transforms": [
{ "PathPattern": "/api/v2/{**remainder}" }
]
}
}
Service Routing
{
"users-service": {
"ClusterId": "users-cluster",
"Match": {
"Path": "/gateway/users/{**remainder}"
},
"Transforms": [
{ "PathPattern": "/api/{**remainder}" }
]
},
"orders-service": {
"ClusterId": "orders-cluster",
"Match": {
"Path": "/gateway/orders/{**remainder}"
},
"Transforms": [
{ "PathPattern": "/api/{**remainder}" }
]
}
}
Legacy API Support
{
"legacy-redirect": {
"ClusterId": "modern-api-cluster",
"Match": {
"Path": "/legacy/{controller}/{action}/{id?}"
},
"Transforms": [
{ "PathPattern": "/api/v3/{controller}/{id}" }
]
}
}
Header Transforms
Header transforms allow you to add, modify, or remove HTTP headers on requests and responses.
Request Header Transforms
RequestHeader - Set
Sets or replaces a request header:
{
"Transforms": [
{ "RequestHeader": "X-API-Key", "Set": "secret-key-123" },
{ "RequestHeader": "Authorization", "Set": "Bearer internal-token" }
]
}
RequestHeader - Append
Adds an additional header value:
{
"Transforms": [
{ "RequestHeader": "Accept-Encoding", "Append": "br" }
]
}
RequestHeaderRemove
Removes a header completely:
{
"Transforms": [
{ "RequestHeaderRemove": "X-Internal-Debug" }
]
}
Response Header Transforms
ResponseHeader - Set
{
"Transforms": [
{ "ResponseHeader": "X-Powered-By", "Set": "ApiCharge" },
{ "ResponseHeader": "Cache-Control", "Set": "public, max-age=3600" }
]
}
ResponseHeader - Append
{
"Transforms": [
{ "ResponseHeader": "Access-Control-Allow-Origin", "Append": "https://app.example.com" }
]
}
ResponseHeaderRemove
{
"Transforms": [
{ "ResponseHeaderRemove": "Server" },
{ "ResponseHeaderRemove": "X-AspNet-Version" }
]
}
Conditional Header Transforms
Use the When
parameter to control when response headers are applied:
{
"Transforms": [
{
"ResponseHeader": "X-Cache-Status",
"Set": "HIT",
"When": "Success"
},
{
"ResponseHeader": "X-Error-Source",
"Set": "Gateway",
"When": "Failure"
}
]
}
Valid When
values:
Success
- Only for successful responses (2xx status codes)Failure
- Only for error responses (4xx, 5xx status codes)Always
- For all responses (default)
Query Parameter Transforms
Query parameter transforms allow manipulation of URL query strings.
QueryValueParameter
Adds or modifies query parameters with static values:
{
"Transforms": [
{ "QueryValueParameter": "source", "Set": "gateway" },
{ "QueryValueParameter": "timestamp", "Append": "2024-01-01" }
]
}
QueryRouteParameter
Adds query parameters using route values:
{
"Match": {
"Path": "/api/{service}/{version}/{**remainder}"
},
"Transforms": [
{ "QueryRouteParameter": "svc", "Append": "service" },
{ "QueryRouteParameter": "ver", "Append": "version" }
]
}
QueryRemoveParameter
Removes specific query parameters:
{
"Transforms": [
{ "QueryRemoveParameter": "debug" },
{ "QueryRemoveParameter": "internal_token" }
]
}
Query Transform Examples
Adding Tracking Parameters
{
"analytics-tracking": {
"ClusterId": "api-cluster",
"Match": {
"Path": "/api/{**remainder}"
},
"Transforms": [
{ "QueryValueParameter": "utm_source", "Set": "apigateway" },
{ "QueryValueParameter": "utm_medium", "Set": "proxy" }
]
}
}
API Key Injection
{
"backend-auth": {
"ClusterId": "protected-cluster",
"Match": {
"Path": "/api/{**remainder}"
},
"Transforms": [
{ "QueryValueParameter": "api_key", "Set": "backend-service-key" },
{ "QueryRemoveParameter": "client_secret" }
]
}
}
Security and Forwarding Transforms
These transforms handle security-related headers and client information forwarding.
X-Forwarded Headers
Control which X-Forwarded-* headers are included:
{
"Transforms": [
{
"X-Forwarded": "For,Proto,Host",
"HeaderPrefix": "X-Forwarded-"
}
]
}
Available X-Forwarded options:
For
- Client IP addressProto
- Original protocol (http/https)Host
- Original host headerPrefix
- Original path baseAll
- Include all headers (default)Off
- Disable all X-Forwarded headers
RequestHeaderOriginalHost
Preserves the original host header:
{
"Transforms": [
{ "RequestHeaderOriginalHost": "true" }
]
}
ClientCert
Forwards client certificates as Base64-encoded headers:
{
"Transforms": [
{ "ClientCert": "X-Client-Certificate" }
]
}
RequestHeadersCopy and ResponseHeadersCopy
Control which headers are copied from client to backend:
{
"Transforms": [
{ "RequestHeadersCopy": "true" },
{ "RequestHeadersAllowed": "Authorization,Content-Type,Accept" }
]
}
Advanced Transform Scenarios
Microservice Gateway Pattern
{
"user-service": {
"ClusterId": "users-cluster",
"Match": {
"Path": "/gateway/users/{**remainder}"
},
"Transforms": [
{ "PathPattern": "/api/{**remainder}" },
{ "RequestHeader": "X-Service-Name", "Set": "users" },
{ "RequestHeader": "X-Gateway-Version", "Set": "v1.0" },
{ "QueryValueParameter": "source", "Set": "gateway" },
{ "ResponseHeader": "X-Service-Response-Time", "Append": "users-service" }
]
}
}
Legacy API Modernization
{
"legacy-soap-to-rest": {
"ClusterId": "modern-api-cluster",
"Match": {
"Path": "/soap/{service}.asmx/{method}",
"Methods": ["POST"]
},
"Transforms": [
{ "PathPattern": "/api/v2/{service}/{method}" },
{ "RequestHeader": "Content-Type", "Set": "application/json" },
{ "RequestHeader": "Accept", "Set": "application/json" },
{ "QueryRouteParameter": "legacy", "Set": "true" }
]
}
}
Multi-Tenant Routing
{
"tenant-routing": {
"ClusterId": "multi-tenant-cluster",
"Match": {
"Path": "/api/{tenant}/{**remainder}"
},
"Transforms": [
{ "PathPattern": "/api/{**remainder}" },
{ "RequestHeader": "X-Tenant-ID", "Append": "tenant" },
{ "QueryRouteParameter": "tenant_id", "Append": "tenant" },
{ "ResponseHeader": "X-Tenant-Context", "Append": "tenant" }
]
}
}
Load Balancer Health Checks
{
"health-check": {
"ClusterId": "api-cluster",
"Match": {
"Path": "/health"
},
"Transforms": [
{ "PathSet": "/api/system/health" },
{ "RequestHeader": "X-Health-Check", "Set": "gateway" },
{ "ResponseHeader": "Cache-Control", "Set": "no-cache" }
]
}
}
Transform Order and Precedence
Transforms are applied in the order they appear in the configuration array. Understanding this order is important for complex transformations.
Execution Order
- Path transforms are applied first
- Query parameter transforms are applied next
- Request header transforms are applied before forwarding
- Response header transforms are applied to the response
Order-Dependent Example
{
"Transforms": [
{ "PathRemovePrefix": "/api/v1" },
{ "PathPrefix": "/api/v2" },
{ "QueryValueParameter": "version", "Set": "v2" },
{ "RequestHeader": "Accept", "Set": "application/json" }
]
}
For a request to /api/v1/users?format=xml
:
- Remove prefix:
/users?format=xml
- Add prefix:
/api/v2/users?format=xml
- Add query param:
/api/v2/users?format=xml&version=v2
- Set header:
Accept: application/json
Debugging Transforms
Enable Debug Headers
Add debug headers to see transformation results:
{
"Transforms": [
{ "RequestHeader": "X-Debug-Original-Path", "Set": "{path}" },
{ "RequestHeader": "X-Debug-Original-Host", "Set": "{host}" },
{ "PathPattern": "/api/v2/{**remainder}" },
{ "ResponseHeader": "X-Debug-Final-Path", "Set": "/api/v2/{**remainder}" }
]
}
Logging Transforms
Use header transforms to add correlation IDs for logging:
{
"Transforms": [
{ "RequestHeader": "X-Correlation-ID", "Set": "{guid}" },
{ "RequestHeader": "X-Request-Timestamp", "Set": "{timestamp}" },
{ "ResponseHeader": "X-Correlation-ID", "Append": "{guid}" }
]
}
Best Practices
Performance Considerations
- Minimize transforms - Each transform adds processing overhead
- Order efficiently - Place most important transforms first
- Avoid redundant transforms - Don't set the same header multiple times
- Use caching headers - Add appropriate caching headers in response transforms
Security Guidelines
- Remove sensitive headers - Strip internal headers before forwarding
- Validate input - Be careful with dynamic header values
- Use HTTPS - Ensure secure communication for sensitive transforms
- Sanitize responses - Remove server information in response headers
Maintenance Tips
- Document transforms - Add comments explaining complex transformations
- Test thoroughly - Verify transforms work as expected
- Monitor impact - Track performance impact of transforms
- Version carefully - Consider backward compatibility when changing transforms
Common Pitfalls
- Path segment boundaries - PathRemovePrefix only works on segment boundaries
- Header overrides - Later transforms override earlier ones for the same header
- Case sensitivity - Header names are case-insensitive, but values may be case-sensitive
- Query parameter encoding - Be aware of URL encoding in query parameters
Testing Transforms
Use these techniques to test your transform configurations:
Command Line Testing
# Test path transforms
curl -v https://your-apicharge-instance.com/api/v1/users/123
# Test header transforms
curl -v -H "Accept: application/xml" \
https://your-apicharge-instance.com/api/users
# Test query parameter transforms
curl -v "https://your-apicharge-instance.com/api/users?format=json&debug=true"
# View response headers
curl -I https://your-apicharge-instance.com/api/users
Debug Transform Configuration
{
"debug-route": {
"ClusterId": "echo-cluster",
"Match": {
"Path": "/debug/{**remainder}"
},
"Transforms": [
{ "RequestHeader": "X-Original-Path", "Set": "{path}" },
{ "RequestHeader": "X-Original-Query", "Set": "{query}" },
{ "PathPattern": "/echo/{**remainder}" },
{ "ResponseHeader": "X-Transform-Applied", "Set": "true" }
]
}
}
Next Steps
Now that you understand transforms, explore:
- Route Matching - How routes are selected for transformation
- Rate Limiters - Quality of service controls that work with transforms
- Clustering - How transforms work in distributed deployments