Payload & Header Transformation Engine for API Gateways
Transform HTTP message payloads and headers declaratively — no code changes per API. Deploy as a standalone reverse proxy or embed as a plugin in your existing API gateway.
message-xform is a gateway-agnostic transformation engine that rewrites HTTP request and response messages based on declarative YAML configuration. It sits between your clients and backend APIs, transforming JSON payloads, manipulating headers, rewriting URLs, and mapping status codes — all without touching application code.
The problem it solves: API gateways often need to transform messages between different formats — legacy systems speaking one schema, modern APIs expecting another, headers that need to be promoted into payloads or vice versa. Instead of writing custom code for each gateway product, message-xform provides a single transformation engine that works across all of them.
Run message-xform as an independent HTTP proxy with zero external dependencies. Ideal for:
- Development & testing — validate transforms locally before deploying to a gateway
- Kubernetes sidecar — run alongside your backend in the same pod
- Gateway-free environments — when you don't need a full API gateway
Key features:
- Docker image (~100 MB) with multi-stage build
- TLS termination (inbound + outbound) with mTLS support
- Environment variable overrides for all configuration
- Health (
/health) and readiness (/ready) endpoints - Hot reload via file watcher + admin API (
POST /admin/reload) - Connection pooling with configurable timeouts
# Quick start with Docker
docker run -v ./specs:/specs -v ./config.yaml:/config.yaml \
-p 8080:8080 message-xform-proxy
# Or run directly with Java 21
java -jar message-xform-proxy.jarEmbed message-xform directly into your existing API gateway as a native plugin, rule, or filter. The core engine runs inside the gateway's JVM — no network hop, no sidecar overhead.
Supported gateways:
| Gateway | Integration Model | Status |
|---|---|---|
| Standalone Proxy | Embedded HTTP proxy (Javalin/Jetty) | ✅ Complete |
| PingAccess | Java plugin via RuleInterceptor SPI | 🔲 Planned (Tier 2) |
| PingGateway | Java/Groovy filter chain | 🔲 Planned (Tier 2) |
| WSO2 API Manager | Java extension API | 🔲 Planned (Tier 3) |
| Apache APISIX | Java Plugin Runner | 🔲 Planned (Tier 3) |
| Kong | Sidecar proxy (Lua ecosystem) | 🔲 Planned (Tier 4) |
| NGINX | Sidecar proxy (njs/C ecosystem) | 🔲 Planned (Tier 4) |
Tier 1–3 gateways support direct Java integration — the core engine runs natively inside the gateway. Tier 4 gateways use a sidecar pattern — the standalone proxy runs alongside the gateway, which proxies through it.
message-xform operates on four dimensions of an HTTP message:
Restructure, rename, and reshape JSON payloads using JSLT expressions — a powerful JSON query and transformation language.
# Transform a legacy payload to a modern API format
transform:
lang: jslt
expr: |
{
"user": {
"firstName": .legacy.first_name,
"lastName": .legacy.last_name,
"email": .contact.email
}
}Add, remove, or rename HTTP headers declaratively. Promote payload fields to headers or inject header values into payloads.
headers:
add:
X-Correlation-ID: "generated-uuid"
remove:
- X-Internal-Debug
rename:
X-Old-Header: X-New-Header
# Promote a JSON field to a header
payload-to-headers:
- source: "$.auth.token"
header: "Authorization"
prefix: "Bearer "
strip: true # Remove from payload after promotionConditionally override HTTP response status codes:
status:
set: 201
when: '$status == 200 and .created != null'Rewrite request paths, query parameters, and HTTP methods — enabling de-polymorphization of dispatch-style endpoints:
# Convert POST /dispatch?action=delete to DELETE /api/users/{id}
url:
path:
expr: '"/api/users/" + string(.resourceId)'
method:
set: DELETE
when: '.action == "delete"'Define both request and response transformations in a single spec:
transform:
expr: '{ "name": .legacy_name }' # Request: legacy → modern
reverse:
expr: '{ "legacy_name": .name }' # Response: modern → legacyTransforms are defined in YAML spec files — one per transformation — and bound to URL patterns via profiles:
# specs/user-transform.yaml — the transformation logic
id: user-transform
version: 1.0.0
input:
schema: { type: object }
output:
schema: { type: object }
transform:
lang: jslt
expr: '{ "user": { "name": .legacy.name } }'
headers:
add:
X-Transformed: "true"# profile.yaml — binds specs to routes
profile: my-api
transforms:
- spec: user-transform@1.0.0
match:
path: /api/v1/users/**
method: [POST, PUT]
content-type: application/json
direction: requestKey design principles:
- Specs are portable — pure transformation logic, no gateway knowledge
- Profiles are deployment-specific — bind specs to URL patterns per environment
- Version pinning — reference specs by
id@versionfor safe concurrent upgrades - Hot reload — update specs without restarting the proxy or gateway
- Java 21 (for building from source)
- Docker (for containerized deployment)
git clone https://github.com/ivanstambuk/message-xform.git
cd message-xform
# Run the full quality gate (format + compile + test)
./gradlew --no-daemon spotlessApply check
# Build the standalone proxy shadow JAR
./gradlew --no-daemon :adapter-standalone:shadowJar
# Build the Docker image (~100 MB)
docker build -t message-xform-proxy adapter-standalone/# With Docker
docker run \
-v ./my-specs:/specs \
-v ./my-profile.yaml:/profile.yaml \
-e MXFORM_BACKEND_HOST=api.example.com \
-e MXFORM_BACKEND_PORT=443 \
-e MXFORM_BACKEND_TLS_ENABLED=true \
-p 8080:8080 \
message-xform-proxy
# Or directly with Java
java -jar adapter-standalone/build/libs/adapter-standalone-*-all.jarmessage-xform/
├── core/ # Gateway-agnostic transformation engine
│ ├── model/ # TransformSpec, TransformProfile, Message
│ ├── engine/ # TransformEngine, TransformRegistry
│ ├── spi/ # ExpressionEngine, TelemetryListener
│ └── schema/ # JSON Schema validation
├── adapter-standalone/ # Standalone HTTP reverse proxy
│ ├── adapter/ # GatewayAdapter SPI implementation
│ ├── proxy/ # ProxyHandler, UpstreamClient, FileWatcher
│ ├── config/ # YAML config loader with env var overlay
│ └── tls/ # TLS/mTLS configuration
├── docs/ # Specifications, ADRs, research
│ ├── architecture/ # Feature specs, roadmap, terminology
│ ├── decisions/ # Architecture Decision Records (ADRs)
│ └── research/ # Gateway evaluations, API analysis
└── Dockerfile # Multi-stage build (~100 MB image)
| Component | Technology |
|---|---|
| Language | Java 21 (virtual threads) |
| Build | Gradle 8.12 (Kotlin DSL) |
| HTTP Server | Javalin 6 / Jetty 12 |
| JSON Processing | Jackson |
| Transform Language | JSLT (pluggable SPI) |
| Schema Validation | networknt json-schema-validator |
| Testing | JUnit 5, AssertJ |
| Formatting | Palantir Java Format (via Spotless) |
| CI | GitHub Actions |
Licensed under the Apache License 2.0.
Built with ☕ Java 21 · Declarative YAML · Zero-dependency core

