Quickly modernizing SOAP APIs

Date

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 APIs (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 and Taxi to annotate the SOAP APIs.

Taxi - A quick intro

Taxi is a relatively new entrant in the API space. 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.

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

The code for this blog is available on Github

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

Background

We’re working with a public webservice at Oorsprong.org that provides Country Information.

There’s lots of different services, which return small slices of information about countries, given an ISO code.

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

Adding Taxi Metadata to SOAP

Taxi metadata allows us to sprinkle a few additional tags into our WSDL which let mark data attributes that are the same.

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

Countries.wsdl
<!-- 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 com.demo.IsoCountryCode, and I’ll get back a message containing a com.demo.CountryName

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

Countries.wsdl
<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.

Using Taxi queries to get the data we want

Now that our WSDL is annotated with Taxi, we can get Orbital 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
 }

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",
}

Orbital’s profiler shows us exactly what happened:

Orbital's profiler shows the sequence of calls issued to load the data we needed
Orbital's profiler shows the sequence of calls issued to load the data we needed

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

Drilling into each call shows the request and response
Drilling into each call shows the request and response

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:

CountryData.query.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:

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

Gives us:

[
  {
    "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

Instant REST API, that live reloads
Instant REST API, that live reloads

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 reloads
Live reloads

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.

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