Converting SOAP APIs to REST, without code 🧼🧰🪄
Tags: api, tutorial, microservices, data
Published: 2023-09-05 17:21:53

#### TLDR:
 * Soap is an outdated API technology, that takes LOTS of boilerplate.  Sadly, sometimes you don't have a choice.
 * We can quickly wrap a SOAP API into REST without code, making it much easier to work with in our web apps
 * We can also combine multiple SOAP APIs into a single, purpose-built REST API.

---

If you've ever had to work with SOAP APIs, you know how unpleasant they can be.

They're cumbersome to call, have heavy amounts of code-gen required, and if you're not using Java or C#... you're already kinda screwed.

We're gonna to take an old SOAP API (which, lets face it, aren't fun to play with), and repackage it - combining multiple calls into a single REST API.

Instead of writing piles of boilerplate plumbing code, we'll automate all the integration work with [Orbital](https://orbitalhq.com) (an open-source data integration platform) and [Taxi](https://taxilang.org) to annotate the SOAP APIs.


![A man staring at a nightmare washing machine](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6a5r5osmgvkaiy10sj1v.png)

> Soap...the stuff of nightmares.

---

## Building a Country Info Application
We're working with a public webservice at [Oorsprong.org](http://webservices.oorsprong.org/websamples.countryinfo/CountryInfoService.wso) that provides Country Information.

It's a powerful API that exposes lots of information about countries, but each piece of information needs a separate call to a SOAP API.

Each API code takes an ISO Code (eg., `GBP` / `USA` / `NZ`), and returns a small slice of information about a country. (Flag, Currency, Capital City).

We'd like to grab the data of a few of these services, but without having to write a bunch of SOAP code.


---

## Taxi - A quick intro
We'll be using [Taxi](https://taxilang.org) to help modernize and slice-n-dice this API.  If you haven't heard of Taxi before - you're not alone.


![A man driving a taxi](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n8amz1vh8e8p57da17dz.png)
 
Taxi is a relatively new entrant in the API space.  It's an open source metadata language for describing APIs.

It's goal is to let developers add simple, (but type-safe) [tags](https://orbitalhq.com/blog/2023-05-22-semantic-metadata-101) into their APIs, so software can understand how different APIs relate to one another.

It's a simple way of saying "This field is the same as that field", while keeping systems decoupled.

And the same tags we add into our APIs, we can use to query for data using TaxiQL - Taxi's query language - which replaces all the integration plubming code we'd normally have to write.

---

### Getting going
Let's go a little deeper, and break that down into three steps:

 1. Adding Taxi metadata to the SOAP WSDL ([here's](https://orbitalhq.com/docs/describing-data-sources/soap) the relevant docs)
 2. Using [Taxi queries](https://orbitalhq.com/docs/querying/writing-queries) to build the responses we want to return
 3. Publishing those queries as REST APIs, with LIVE RELOAD FTW!

The code for this blog is available on [Github](https://github.com/orbitalapi/demos/tree/main/soap-demo)


If you find this tutorial useful, why not give us a star on [Github](https://github.com/orbitalapi/orbital)

---

### Adding metadata to SOAP for fun & profit.
Taxi metadata allows us to sprinkle a few additional tags into our WSDL which let mark data attributes that are the same.

Later we can use these tags to automate all the SOAP interaction, so we don't have to write any SOAP code ourselves.

For example, our CountryInfo wsdl has a section dedicated to request / response payloads.

```diff

<!-- Snippet from our WSDL - Request / Response messages for getting a Country Name -->
<xs:element name="CountryName">
    <xs:complexType>
       <xs:sequence>
          <xs:element name="sCountryISOCode"
                 type="xs:string" 
+                taxi:type="com.demo.IsoCountryCode"/>
       </xs:sequence>
    </xs:complexType>
    </xs:element>
    <xs:element name="CountryNameResponse">
    <xs:complexType>
       <xs:sequence>
         <xs:element name="CountryNameResult" 
             type="xs:string" 
+            taxi:type="com.demo.CountryName"/>
       </xs:sequence>
    </xs:complexType>
</xs:element>
```

You can summarize this by saying:

> I can send a `CountryName` request containing a `IsoCountryCode`, and I'll get back a message containing a `CountryName`

Elsewhere, we can also use a `IsoCountryCode` to get more information, like a `CountryFlagUrl`:

```diff

<xs:element name="CountryFlag">
  <xs:complexType>
     <xs:sequence>
        <xs:element name="sCountryISOCode"
            type="xs:string"
+           taxi:type="com.demo.IsoCountryCode"/>
     </xs:sequence>
  </xs:complexType>
</xs:element>
<xs:element name="CountryFlagResponse">
  <xs:complexType>
     <xs:sequence>
        <xs:element name="CountryFlagResult"
                type="xs:string"
+               taxi:type="com.demo.CountryFlagUrl"/>
     </xs:sequence>
  </xs:complexType>
</xs:element>
```

That's a whole lotta XML, for not much info - which is one of the problems with SOAP - it's just too darn noisy.


![Sarah Lynn - That's Too Much, Man](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kav73g662ar8qpe5gdaj.gif)

---

## Using Taxi queries to get the data we want
Now that our WSDL is annotated with Taxi, we can use TaxiQL to do all the heavy lifting for us.

Rather than generating a bunch of Java classes from the WSDL, we can simply write a taxi query:

```taxi
given { iso: IsoCountryCode = "NZ"}
find { 
    // Each field comes from a different SOAP service.
    // But taxi keeps this nice and succinct, composing the services
    // together we need on demand
    name: CountryName
    flag: CountryFlagUrl
    currency: CurrencyName
 }
```

We can send that query to Orbital, and Orbital calls all the SOAP services we need on our behalf, giving us back the data we're looking for:

```json
{
   "name": "New Zealand",
   "flag": "http://www.oorsprong.org/WebSamples.CountryInfo/Flags/New_Zealand.jpg",
   "currency": "New Zealand Dollars",
}
```

Orbital's profiler shows us exactly what happened:

![Oribtal Profiler](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4q2i9cycf2o838j8ruw8.png)

We can see:
 * Orbital called 3 different SOAP endpoints
 * It dealt with all the SOAP Request/Response malarchy
 * It plucked the data we want, and gave it back.

And we can drill into the details of each call too:


![Call details](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gqpuowrtyibspn9m91gd.png)

---

## Instant REST API
Now that we're happy with the content of our payload, we can quickly turn it into a REST API.

Simply by checking a query into our project's git repo, and adding an annotation, Orbital gives us a fully working REST API:

```taxi

@HttpOperation(url = '/api/q/countrydata/{countryCode}', method = 'GET')
query countrydata(@PathVariable("countryCode") countryCode :  IsoCountryCode) {
   given { countryCode }
   find {
       name : CountryName
       flag: CountryFlagUrl
       currency: CurrencyName
       capital: CapitalCityName
   }
}
```

By adding a `@HttpOperation` annotation to our query, and saving it our local Taxi project, Orbital instantly exposes a REST API for us.
Rather than hard-coding our query to information about New Zealand, we've put the country code as a part of the path.

```
@HttpOperation(url = '/api/q/countrydata/{countryCode}', method = 'GET')
```

So, if we call:

```bash
curl http://localhost:9022/api/q/countrydata/NZ 
```

Gives us:

```json

[
  {
    "name": "New Zealand",
    "flag": "http://www.oorsprong.org/WebSamples.CountryInfo/Flags/New_Zealand.jpg",
    "currency": "New Zealand Dollars",
    "capital": "Wellington"
  }
]
```

---

## Live reload for the win


Orbital is live reloading this query, so while we're working locally and making changes to our query, changes are instantly deployed!

Once deployed, making changes is as simple as pushing to a git repository.

Check it out:
![Live reload](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xlmdcmz8dauc2jsp8pxe.gif)

Adding the `CapitalCityName` into our request actually added a whole new SOAP request and integration into our query, which Orbital instantly reloaded behind the scenes.

---

## Conclusion

In this blog, we've quickly repackaged a SOAP API into the exact API we wanted, without having to deal with any of the nastiness that 
normally comes from dealing with SOAP requests.

In the process, we've touched on a few of Orbital's handy features:

 * Using Taxi metadata in SOAP WSDLs
 * Composing together multiple SOAP calls
 * Instant REST APIs with live reload
 * Viewing profiling data 

The full code to run this demo locally is available on [Github](https://github.com/orbitalapi/demos/tree/main/soap-demo)

This is only scratching the surface of what we can do.  Orbital can also stitch this data together with other APIs (such as [REST](https://orbitalhq.com/docs/describing-data-sources/open-api) / [gRPC](https://orbitalhq.com/docs/describing-data-sources/protobuf)), call [serverless functions](https://orbitalhq.com/docs/describing-data-sources/aws-services#lambda), query our [Databases](https://orbitalhq.com/docs/describing-data-sources/databases), or [Kafka queues](https://orbitalhq.com/docs/describing-data-sources/connect-kafka-topic).

---

### Give us star!


![A small boy asking for a star](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ls1d8zm26pvbxat92j6w.png)

If you found this useful, please give our [Github repo](https://github.com/orbitalapi/orbital) a star!