Converting SOAP APIs to REST, without code 🧼🧰πŸͺ„

martypitt

Marty Pitt

Posted on September 5, 2023

Converting SOAP APIs to REST, without code 🧼🧰πŸͺ„

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 (an open-source data integration platform) and Taxi to annotate the SOAP APIs.

A man staring at a nightmare washing machine

Soap...the stuff of nightmares.


Building a Country Info Application

We're working with a public webservice at Oorsprong.org 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 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

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 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 the relevant docs)
  2. Using Taxi 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

If you find this tutorial useful, why not give us a star on Github


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.


<!-- 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>
Enter fullscreen mode Exit fullscreen mode

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:


<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>
Enter fullscreen mode Exit fullscreen mode

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


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:

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
 }
Enter fullscreen mode Exit fullscreen mode

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:

{
   "name": "New Zealand",
   "flag": "http://www.oorsprong.org/WebSamples.CountryInfo/Flags/New_Zealand.jpg",
   "currency": "New Zealand Dollars",
}
Enter fullscreen mode Exit fullscreen mode

Orbital's profiler shows us exactly what happened:

Oribtal Profiler

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


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:


@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
   }
}
Enter fullscreen mode Exit fullscreen mode

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')
Enter fullscreen mode Exit fullscreen mode

So, if we call:

curl http://localhost:9022/api/q/countrydata/NZ 
Enter fullscreen mode Exit fullscreen mode

Gives us:


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

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

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

This is only scratching the surface of what we can do. Orbital can also stitch this data together with other APIs (such as REST / gRPC), call serverless functions, query our Databases, or Kafka queues.


Give us star!

A small boy asking for a star

If you found this useful, please give our Github repo a star!

πŸ’– πŸ’ͺ πŸ™… 🚩
martypitt
Marty Pitt

Posted on September 5, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related