Quickly modernizing SOAP APIs
- Date
- Marty Pitt
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:
- Adding Taxi metadata to the SOAP WSDL (here’s the relevant docs)
- Using Taxi queries to build the responses we want to return
- Publishing those queries as REST APIs
The code for this blog is available 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.
<!-- 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 acom.demo.IsoCountryCode
, and I’ll get back a message containing acom.demo.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>
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:
And we can drill into the details of each call too:
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
}
}
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
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:
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!