Consuming SOAP API with NODE.js

I have no idea why you or anyone would prefer SOAP over REST using Node.js. The reason why I had to do it was that one of the clients had an existing infrastructure built with SOAP and he wanted to continue using it.

Now the easiest way to use SOAP in node.js is using the node soap module. The link to the module is here. We will be consuming the soap API in this tutorial. In the next one though we would be creating a SOAP API.

Getting Started with SOAP using Node.js

Install the node soap package using npm.

npm i soap

This will add the soap module in your package. Now you need the endPoint for the soap package. I am assuming that you would be posting some data using the soap service. You can make necessary changes and would be able to retrieve the data.

Let us first check what services are present in the soap API. Now you could directly check the same using the WSDL provided to you but we will also learn to check the services and their structure using the describe method this soap module provides us and then we will construct the code to use those services.

Now check with the type of authentication that was implemented in the service and also if WSDL is password protected. In my case, it was protected with NTLM. So, I had to pass the wsdl_options object to the createClient method provided by our node soap package.

Structure of the wsdl_options object

            var wsdl_options= {
              ntlm: true,
              username: "your username",
              password: "your password",
              domain: "domain",
              workstation :"workstation"
            }

Creating the Client.

soap.createClient(data.credentials[data.type], {wsdl_options},
function (err, client) {
              console.log(client.describe());
});

The client.describe() method returns us all the services in the WSDL URL mentioned. you can get to know the argument and its structure that the service is expecting. This function returns us an object so you can query the object as you would query any other object in javascript.

Now, most probably the service would require us to use some form of authorization to access the services. In my case, it was NTLM so I need to set this security to the client.

client.setSecurity(new soap.NTLMSecurity(data.credentials.username, data.credentials.password, data.credentials.domain));

//In case it was Basic Authorization i would need to set 
  client.setSecurity(new soap.BasicAuthSecurity('username', 'password'));

To check other type security mechanism check here.

Now you just need to call the service with correct arguments that you discovered earlier by either using wsdl or the describe method on the client.

client[`Service`][`Port`][`Service_name`](data, function (err, response) {
                console.log(`${data.type} response`, response)
              })

Now the structure you see above to call the service was how my WSDL was laid out. Yours could be different so Its important to know the services and their arguments before you run this code. Now the best part is using that we could directly pass a JSON object (data) and it internally creates the XML to send.

Constructing the code to use SOAP API

const soap = require('soap');      
class SOAP_REQUEST {
        constructor(obj){
          this.apiName = obj.apiName;
          this.type = obj.type;
          this.data = obj.data;
        }
        makeRequest(data) {
          return new Promise(async (resolve, reject) => {
            var wsdl_options= {
              ntlm: true,
              username: data.credentials.username,
              password: data.credentials.password,
              domain: data.credentials.domain
            }
            soap.createClient(data.credentials[data.type], {wsdl_options}, function (err, client) {
              if (err) {
                reject(err);
              }
              client.setSecurity(new soap.NTLMSecurity(data.credentials.username, data.credentials.password, data.credentials.domain));
              client[`${data.type}_Service`][`${data.type}_Port`][data.apiName](data.body.type_data, function (err, response) {
                console.log(`${data.type} response`, response)
                resolve(response);
              })
            });
          })
        }
        sendData() {
          return new Promise(async (resolve, reject)=>{
            try {
              var data={body:{}};
              data.apiName = this.apiName;
              data.type = this.type;
              data.body.type_data = this.data;
              data.credentials = {
                username: "username",
                password: "password",
                domain: "domain",
                Service_1 :"Service_1 EndPoint",
              }
              await this._makeRequest(data);
              resolve(true);
            }catch (error) {
              resolve(false);
            }
          })
        }
      }

Using our SOAP_REQUEST Class

var execute = async function(service, apiname){
var headerObj = {
          type: service,
          apiName:apiname,
          data:{
            [service]:{
              "Key" : "",
              "No":"NO",
              "Data_1": "data_1",
              "Data_2": "data_2",
              "Data_3": "data_3"
            }
          }
        }
        var headerReq = new SOAP_REQUEST(headerObj);
        var headerResponse = await headerReq.sendData();
}

Quick Walkthrough of the code.

We created a class SOAP_REQUEST. That accepts API name, Type which is the name of the service, and the data that we will pass as arguments to the service.

Our makeRequest method accepts data as the only argument. Make this argument optional if few services in your API do not need any arguments to be passed almost all the explanation till now has been about this very method. So I wouldn’t go into all details but you would see a strange-looking line of code

 client[`${data.type}_Service`][`${data.type}_Port`][data.apiName]

Well, that is how the services I was working with were structured and all services in the WSDL had the same naming convention which is the exact reason why you see _Service and _Port hardcoded to the object key’s name. this function returns us the response.

Now comes our sendData function now this function all it does is to make the request with the correct data to the correct service, in the end, there is an execute function that uses our soap class and does the soap request. now a few values in these functions named “Data” are hardcoded ideally pass the data as an argument as well and change the structure of the [service] as per the structure mentioned in the WSDL.

Now execute function is just to show you how to use class methods which is why you don’t see a catch block. Ideally this would be the point where your logic goes.

Leave a Reply