Menu

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.

Note: ApiCharge uses YARP's transform system, which operates on proxy requests and responses without modifying the original client requests. Transforms are applied after route matching but before forwarding to backend services.

Transform Categories

Transforms fall into several categories:

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:

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

Important: PathRemovePrefix only removes the prefix if it matches exactly on path segment boundaries (/). If the prefix doesn't match, no changes are made.

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:

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:

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

  1. Path transforms are applied first
  2. Query parameter transforms are applied next
  3. Request header transforms are applied before forwarding
  4. 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:

  1. Remove prefix: /users?format=xml
  2. Add prefix: /api/v2/users?format=xml
  3. Add query param: /api/v2/users?format=xml&version=v2
  4. 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

Security Guidelines

Maintenance Tips

Common Pitfalls

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: