Middleware
Front aging SOAP / WSDL services with a REST gateway so modern clients stop generating stubs and wrestling with XML envelopes.
SOAP still runs plenty of critical back-ends, but every new client that has to generate WSDL stubs and hand-build XML envelopes pays a tax. A gateway translates REST calls into SOAP requests and maps responses back to JSON, so consumers get a clean modern contract while the back-end stays untouched. This layer handles the envelope, faults, and type coercion.
A working piece from this integration — no sign-up. The full build handles the edge cases, safeguards, and cutover.
SOAP-to-REST gateway: map WSDL operations to resource-based endpoints and transform JSON bodies into SOAP envelopes.
// Express middleware: REST endpoint → SOAP call app.post('/api/customers', async (req, res) => { // Map REST resource → SOAP operation from WSDL const soapEnvelope = `<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"> <soap:Body> <GetCustomerById xmlns="http://legacy.example.com/crm"> <customerId>${req.body.customerId}</customerId> </GetCustomerById> </soap:Body> </soap:Envelope>`; const soapResponse = await fetch('http://legacy-soap.internal/crmservice.asmx', { method: 'POST', headers: { 'Content-Type': 'text/xml', 'SOAPAction': '"http://legacy.example.com/crm/GetCustomerById"' }, body: soapEnvelope }); // Extract SOAP Body content → return as JSON to client const xml = await soapResponse.text(); const json = xmlToJson(xml); // strip soap:Envelope wrapper res.json(json); }); // WSDL operation → REST endpoint mapping POST /api/customers → GetCustomerById POST /api/orders → CreateOrder GET /api/orders/{id} → GetOrderStatus PUT /api/products/{id} → UpdateProduct DELETE /api/customers/{id} → DeleteCustomer
How we'd take this from discovery to a production-safe cutover — the phases, the canonical mapping, and the edge cases that bite.
Front-end existing SOAP/WSDL services with a REST API gateway to eliminate client-side stub generation and XML-envelope handling. This pathway covers WSDL parsing, REST resource modeling, transformation layer configuration, authentication translation, and reversible cutover.
orders.wsdl, customers.wsdl) and referenced XSD schemas. Catalog every <wsdl:operation>, input/output message structure, and SOAP binding style (document/literal vs. RPC/encoded). Identify namespace URIs (e.g., http://schemas.example.com/orders/v1) that must be preserved in the transformation layer.GET for retrieval, POST for creation, PUT/PATCH for updates, DELETE for removal. Define path templates (e.g., GET /v1/orders/{orderId}) and document them in OpenAPI 3.0.SOAPAction header from operation registry. For outbound SOAP-to-REST: parse XML response, strip envelope, extract payload into JSON. Use JSONata or XSLT 3.0 for the mappings; store transformation rules in version-controlled config.Authorization: Bearer, API keys) into equivalent SOAP header constructs (WS-Security wsse:Security block). Map cookies used in SOAP sessions to X-Session-Token custom headers or a gateway-side session store.soapUI. Run round-trip tests: REST request → SOAP call → SOAP response → REST response, comparing fidelity. Capture baseline response times for cutover regression checks.soapOperation, soapFaultPresent) for observability.| SOAP Element / Attribute | REST Equivalent | Transformation Logic | Notes |
|---|---|---|---|
SOAPAction header |
HTTP method + route path | Lookup table: POST /v1/orders → SOAPAction: CreateOrder |
Enforce via gateway plugin |
wsse:Security token block |
Authorization header (JWT) |
Extract wsse:UsernameToken → mint JWT; validate signature against IdP |
Requires IdP integration |
<orders:OrderId> (qualified element) |
Path parameter {orderId} |
XPath //orders:OrderId/text() → $.orderId |
Namespace prefix preserved |
<orders:LineItems> array structure |
JSON array lineItems[] |
Direct map; xsi:type attributes stripped |
Handle empty array as [] |
SOAP-ENV:Fault response |
HTTP 4xx/5xx + JSON error body |
Extract faultcode, faultstring → {"error": "...", "code": 400} |
Map faultcode strings to RFC 7807 format |
Content-Type: text/xml |
Content-Type: application/json |
Transform in gateway response plugin; charset preserved | Set Accept: application/json on upstream rewrite |
ms SOAPSessionId cookie |
Gateway-managed session header | Map to X-Gateway-Session passed to SOAP backend |
Session store TTL must match backend timeout |
xop:Include elements. The gateway must decode these before serializing to REST JSON as a base64 string or a data: URI.Cookie or WS-Session headers break under stateless REST clients. The gateway must inject or strip session headers on every request.<Tag xsi:nil="true"/> where REST expects the field omitted. The gateway must normalize nil values to JSON null or field omission per API contract.Make the cutover fully reversible by maintaining the original SOAP endpoint URLs alongside the new REST routes during a traffic-splitting window. Use the gateway's canary routing (e.g., weight: 5 for REST, weight: 95 for legacy) and increment only after error-rate checks pass for a sustained window. Before promoting to 100%, run a reconciliation job: for a random sample of requests, replay both the REST and the original SOAP path and diff the response payloads field-by-field, flagging any divergence above a configured tolerance threshold. If divergence exceeds tolerance or error rates spike, revert the route weights via automated policy. Retain transformation configuration in version-controlled artifacts so a rollback restores the exact prior mapping table in seconds.
Reading the reference is free. Delivering it under liability — with the safeguards that keep production running through the cutover — is what we do.