Express SOAP Middleware

raw JSON →
1.1.4 verified Thu Apr 23 auth: no javascript maintenance

`express-soap` is an Express middleware designed to streamline the creation of SOAP web services within Node.js applications, leveraging the underlying `node-soap` library. Its current stable version is 1.1.4, published approximately two years ago, suggesting a stable or maintenance release cadence rather than rapid iteration. The primary problem it solves is the inconvenient and potentially problematic integration of `node-soap`'s server listener directly with Express, particularly concerning middleware order and convenience. By providing a standard Express middleware, `express-soap` allows developers to mount SOAP endpoints at specific paths (e.g., `app.use('/soap/path', soap(...))`) in a familiar and predictable manner, addressing a common pain point in `node-soap`'s direct usage. It exposes all `node-soap` options directly, ensuring full compatibility while abstracting away boilerplate involved in setting up SOAP listeners, making it a key differentiator for applications requiring SOAP interoperability with an Express backend.

error Error: Cannot find module 'express' or Error: Cannot find module 'soap'
cause `express` or `soap` are peer dependencies and must be installed explicitly in your project, not just implicitly by `express-soap`.
fix
Run npm install express soap in your project's root directory to install the required peer dependencies.
error SOAP Fault: Server Error (or similar generic SOAP fault from client)
cause This often indicates an unhandled exception occurred within one of your SOAP service methods on the server, or the `res` callback was not correctly invoked, leading to a server-side error that propagates as a generic SOAP fault.
fix
Review the server logs for uncaught exceptions within your SOAP service methods. Ensure all service methods handle potential errors gracefully and call the res callback with an error object or a valid response under all circumstances.
error TypeError: Cannot read properties of undefined (reading 'MyOperation')
cause The `services` object passed to `express-soap` does not accurately map to the structure (Service Name -> Port Name -> Operation Name) defined in your WSDL, or the WSDL itself is malformed/inaccessible.
fix
Verify that your services object precisely mirrors the structure of your WSDL, paying close attention to case sensitivity and hierarchy (e.g., services: { ServiceName: { PortName: { MyOperation: ... } } }). Double-check the WSDL for correctness and accessibility.
breaking `express-soap` has a peer dependency on `express` version `4.x`. While `express` v5 has reached beta, using it with `express-soap` may lead to compatibility issues or unexpected behavior, as the middleware explicitly targets the 4.x API.
fix Ensure your project's `express` version is explicitly set to `^4.x.x` in `package.json`. If you must use Express v5, consider alternatives or contribute to `express-soap` for v5 compatibility.
breaking As `express-soap` is built upon `node-soap`, any breaking changes in `node-soap`'s API (especially regarding service definitions, options, or callback signatures) can implicitly affect `express-soap` users. `node-soap` has undergone several changes, including how security, WSDL parsing, and asynchronous operations are handled.
fix Before upgrading the `soap` peer dependency, consult the `node-soap` changelog (e.g., on its GitHub repository) for potential breaking changes relevant to your service implementations. Adapt your `express-soap` service definitions accordingly.
gotcha The provided WSDL or XML definition must be valid and accurately reflect the service methods implemented. Small errors in the WSDL (e.g., incorrect namespaces, types, or operation definitions) can lead to the SOAP service failing to initialize or clients receiving SOAP faults.
fix Thoroughly validate your WSDL/XML schema using a WSDL validator or `node-soap`'s `describe()` method. Ensure that your `services` object structure precisely matches the operations and types defined in your WSDL. If using dynamic WSDL, confirm it is generated correctly.
gotcha SOAP service methods must correctly invoke the `res` callback to send a response back to the client. If `res()` is not called, the SOAP client will typically experience a timeout, leading to an unresponsive service. This applies even for 'one-way' operations where `node-soap` expects a default response.
fix Always ensure that every service method calls the `res` callback at the end of its execution, even if it's `res({})` for an empty response or `res(error)` for an exception. Implement `try-catch` blocks to ensure errors are caught and returned gracefully via `res(error)`.
npm install express-soap
yarn add express-soap
pnpm add express-soap

This quickstart sets up an Express server with two routes: a root HTML page and a SOAP endpoint. The SOAP endpoint exposes an 'Add' operation through a dynamically defined WSDL, demonstrating how to register a service and its methods.

import express from 'express';
import { soap } from 'express-soap';

const app = express();
const PORT = 8000;

// A minimal WSDL definition for demonstration purposes
const myWsdl = `<?xml version="1.0" encoding="UTF-8"?>
<definitions name="CalculatorService" 
             targetNamespace="http://www.examples.com/CalculatorService/"
             xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
             xmlns:tns="http://www.examples.com/CalculatorService/"
             xmlns:xsd="http://www.w3.org/2001/XMLSchema"
             xmlns="http://schemas.xmlsoap.org/wsdl/">

  <types>
    <xsd:schema targetNamespace="http://www.examples.com/CalculatorService/">
      <xsd:element name="AddRequest">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="a" type="xsd:int"/>
            <xsd:element name="b" type="xsd:int"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
      <xsd:element name="AddResponse">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name="result" type="xsd:int"/>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
    </xsd:schema>
  </types>

  <message name="AddInput">
    <part name="parameters" element="tns:AddRequest"/>
  </message>
  <message name="AddOutput">
    <part name="parameters" element="tns:AddResponse"/>
  </message>

  <portType name="CalculatorPort">
    <operation name="Add">
      <input message="tns:AddInput"/>
      <output message="tns:AddOutput"/>
    </operation>
  </portType>

  <binding name="CalculatorBinding" type="tns:CalculatorPort">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <operation name="Add">
      <soap:operation soapAction="Add"/>
      <input>
        <soap:body use="literal"/>
      </input>
      <output>
        <soap:body use="literal"/>
      </output>
    </operation>
  </binding>

  <service name="CalculatorService">
    <port name="CalculatorPort" binding="tns:CalculatorBinding">
      <soap:address location="http://localhost:${PORT}/soap/calculator"/>
    </port>
  </service>

</definitions>`;

app.use('/soap/calculator', soap({
    services: {
        CalculatorService: {
            CalculatorPort: {
                Add: ({ a, b }, res) => {
                    console.log(`Received Add request: a=${a}, b=${b}`);
                    res({
                        result: a + b
                    });
                }
            }
        }
    },
    wsdl: myWsdl
}));

app.get('/', (req, res) => {
    res.send(`
        <h1>Express SOAP Server Running</h1>
        <p>SOAP endpoint at <a href="http://localhost:${PORT}/soap/calculator?wsdl">http://localhost:${PORT}/soap/calculator?wsdl</a></p>
        <p>Try sending a SOAP request to calculate sum (e.g., using SoapUI or a client library).</p>
    `);
});

app.listen(PORT, () => {
    console.log(`Express SOAP server listening on port ${PORT}`);
    console.log(`Access WSDL at http://localhost:${PORT}/soap/calculator?wsdl`);
});