SOAP (Simple Object Access Protocol) is often chosen over REST or GraphQL for scenarios requiring high security, reliability, and formal contracts, particularly in enterprise environments. Its built-in support for WS-Security, strong typing, and detailed error handling makes it ideal for industries like finance or healthcare that demand strict compliance and complex, stateful transactions. SOAP's WSDL provides a clear, formal contract between client and server, ensuring interoperability across different platforms and legacy systems. However, REST and GraphQL are generally preferred for modern web applications due to their simplicity, flexibility, and lower overhead, making them more suitable for mobile or web-based services where performance and ease of use are prioritized. The choice ultimately depends on the specific requirements of the project, with SOAP excelling in structured, secure, and complex use cases.
In previous post we have explores API REST, GraphQL and Websocket network aplicatuib interfaces, now is turn of SOAP. In this post we are going to develop a simple dockerized NodeJS SOAP server that offers an add callculation service.
Addition SOAP Server
First step is to build a SOAP server that will implement arithmethic add operation. The server technology used by its simpicity is a Dockerized NodeJS server. Let’s create a nodeJS server from scratch
npm init -y

Next install server dependencies for soap and express
npm install soap express

This is SOAP server itself:
const express = require('express');
const soap = require('soap');
const http = require('http');
const app = express();
const service = {
MyService: {
MyPort: {
AddNumbers: function (args) {
const aValue = parseInt(args.a , 10);
const bValue = parseInt(args.b , 10);
console.log(' args.a + args.b', aValue + bValue);
return { result: aValue + bValue };
},
},
},
};
const xml = `
<definitions name="MyService"
targetNamespace="http://example.com/soap"
xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:tns="http://example.com/soap"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<message name="AddNumbersRequest">
<part name="a" type="xsd:int"/>
<part name="b" type="xsd:int"/>
</message>
<message name="AddNumbersResponse">
<part name="result" type="xsd:int"/>
</message>
<portType name="MyPort">
<operation name="AddNumbers">
<input message="tns:AddNumbersRequest"/>
<output message="tns:AddNumbersResponse"/>
</operation>
</portType>
<binding name="MyBinding" type="tns:MyPort">
<soap:binding style="rpc" transport="http://schemas.xmlsoap.org/soap/http"/>
<operation name="AddNumbers">
<soap:operation soapAction="AddNumbers"/>
<input>
<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</input>
<output>
<soap:body use="encoded" encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"/>
</output>
</operation>
</binding>
<service name="MyService">
<port name="MyPort" binding="tns:MyBinding">
<soap:address location="http://localhost:8000/wsdl"/>
</port>
</service>
</definitions>
`;
const server = http.createServer(app);
server.listen(8000, () => {
console.log('SOAP server running on http://localhost:8000/wsdl');
});
soap.listen(server, '/wsdl', service, xml);
This Node.js script creates a SOAP web service using the express
and soap
libraries. The service, named MyService
, exposes a single operation called AddNumbers
, which takes two integer arguments (a
and b
), adds them together, and returns the result. The WSDL (Web Services Description Language) XML definition specifies how clients can interact with this service, including the request and response message structures, binding details, and the service endpoint (http://localhost:8000/wsdl
). The server listens on port 8000
, and when a SOAP request is received, it executes the AddNumbers
operation and logs the sum to the console.
The soap.listen()
function attaches the SOAP service to the HTTP server, making it accessible to clients that send SOAP requests. The AddNumbers
function extracts and parses the input arguments from the request, computes their sum, and returns the result in a SOAP response. This setup enables interoperability with SOAP-based clients that conform to the specified WSDL schema.
Lets dockerize server:
# Official image for Node.js
FROM node:18
# Fix working directory
WORKDIR /app
# Copy necessary files
COPY package.json package-lock.json ./
RUN npm install
# Copy rest of files
COPY . .
# Expose server port
EXPOSE 8000
# Command for executing server
CMD ["node", "server.js"]
This Dockerfile sets up a containerized environment for running a Node.js application. It starts with the official Node.js 18 image as the base. The working directory inside the container is set to /app
. It then copies the package.json
and package-lock.json
files into the container and runs npm install
to install dependencies. After that, it copies the rest of the application files into the container. The file exposes port 8000, which is likely used by the Node.js server. Finally, it specifies the command to run the server using node server.js
when the container starts.
Build the image from command line:
docker build -t soap-server .

… and execute the image:
docker run -p 8000:8000 soap-server

Server ready, now is turn of Swift iOS App client.
iOS SOAP Client
iOS app client UI interface is just two textfield for fill in the input operator for the addition, a button for executing the operation and finally a text labed that presents the results.

import SwiftUI
struct ContentView: View {
@State private var result: String = ""
@State private var aStr: String = ""
@State private var bStr: String = ""
var body: some View {
VStack {
Group {
TextField("a", text: $aStr)
TextField("b", text: $bStr)
}
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
Button("Do remote addition") {
guard let aInt = Int(aStr), let bInt = Int(bStr) else {
return
}
callSoapService(a: aInt, b: bInt) { response in
DispatchQueue.main.async {
self.result = response
}
}
} .padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(15)
Text("a+b= \(result)")
.font(.title)
.padding()
}
}
When button is tapped then callSoaService function is being called:
import SwiftUI
struct ContentView: View {
@State private var result: String = ""
@State private var aStr: String = ""
@State private var bStr: String = ""
var body: some View {
VStack {
Group {
TextField("a", text: $aStr)
TextField("b", text: $bStr)
}
.textFieldStyle(RoundedBorderTextFieldStyle())
.padding()
Button("Do remote addition") {
guard let aInt = Int(aStr), let bInt = Int(bStr) else {
return
}
callSoapService(a: aInt, b: bInt) { response in
DispatchQueue.main.async {
self.result = response
}
}
} .padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(15)
Text("a+b= \(result)")
.font(.title)
.padding()
}
}
This Swift function callSoapService(a:b:completion:)
sends a SOAP request to a web service at http://localhost:8000/wsdl
, passing two integers (a
and b
) to the AddNumbers
method. It constructs an XML SOAP message, sends it via an HTTP POST
request, and processes the response asynchronously using URLSession
. The response is parsed to extract the result from the <tns:result>
tag using a regular expression in the extractResult(from:)
function, returning it via a completion handler.
If the web service is running correctly, it should return the sum of a
and b
. However, the function may fail if the server is unavailable, the response format changes, or if the SOAP service requires additional headers. Also, the regular expression parsing method may not be robust against namespace variations.
Build and run iOS app:
Conclusions
SOAP does not provide the flexibility that provides a regular API REST or GraphQL, but for those scenarios wher do we need a very strict API contracts is quite suitable technology.
You can find source code used for writing this post in following repository.
References
- Simple Object Access Protocol (SOAP) 1.1
Protocol specification
- Dealing a REST API with Combine
JaviOS blog post
- Crafting a Simple iOS App using GraphQL API's
JaviOS blog post
- Websockets Made Easy: Create a Simple Chat App in iOS
JaviOS blog post