<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Marty Pitt</title>
    <description>The latest articles on DEV Community by Marty Pitt (@martypitt).</description>
    <link>https://dev.to/martypitt</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F999700%2F941f2783-eb4c-4e35-ba51-dd6bb2413ac8.jpeg</url>
      <title>DEV Community: Marty Pitt</title>
      <link>https://dev.to/martypitt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/martypitt"/>
    <language>en</language>
    <item>
      <title>Converting SOAP APIs to REST, without code 🧼🧰🪄</title>
      <dc:creator>Marty Pitt</dc:creator>
      <pubDate>Tue, 05 Sep 2023 17:21:53 +0000</pubDate>
      <link>https://dev.to/orbitalhq/quickly-modernizing-soap-apis-30gb</link>
      <guid>https://dev.to/orbitalhq/quickly-modernizing-soap-apis-30gb</guid>
      <description>&lt;h4&gt;
  
  
  TLDR:
&lt;/h4&gt;

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




&lt;p&gt;If you've ever had to work with SOAP APIs, you know how unpleasant they can be.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Instead of writing piles of boilerplate plumbing code, we'll automate all the integration work with &lt;a href="https://orbitalhq.com"&gt;Orbital&lt;/a&gt; (an open-source data integration platform) and &lt;a href="https://taxilang.org"&gt;Taxi&lt;/a&gt; to annotate the SOAP APIs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--RoaWAuE9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6a5r5osmgvkaiy10sj1v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--RoaWAuE9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6a5r5osmgvkaiy10sj1v.png" alt="A man staring at a nightmare washing machine" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Soap...the stuff of nightmares.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Building a Country Info Application
&lt;/h2&gt;

&lt;p&gt;We're working with a public webservice at &lt;a href="http://webservices.oorsprong.org/websamples.countryinfo/CountryInfoService.wso"&gt;Oorsprong.org&lt;/a&gt; that provides Country Information.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

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

&lt;p&gt;We'd like to grab the data of a few of these services, but without having to write a bunch of SOAP code.&lt;/p&gt;




&lt;h2&gt;
  
  
  Taxi - A quick intro
&lt;/h2&gt;

&lt;p&gt;We'll be using &lt;a href="https://taxilang.org"&gt;Taxi&lt;/a&gt; to help modernize and slice-n-dice this API.  If you haven't heard of Taxi before - you're not alone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QmCXbFwX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n8amz1vh8e8p57da17dz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QmCXbFwX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n8amz1vh8e8p57da17dz.png" alt="A man driving a taxi" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Taxi is a relatively new entrant in the API space.  It's an open source metadata language for describing APIs.&lt;/p&gt;

&lt;p&gt;It's goal is to let developers add simple, (but type-safe) &lt;a href="https://orbitalhq.com/blog/2023-05-22-semantic-metadata-101"&gt;tags&lt;/a&gt; into their APIs, so software can understand how different APIs relate to one another.&lt;/p&gt;

&lt;p&gt;It's a simple way of saying "This field is the same as that field", while keeping systems decoupled.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;




&lt;h3&gt;
  
  
  Getting going
&lt;/h3&gt;

&lt;p&gt;Let's go a little deeper, and break that down into three steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Adding Taxi metadata to the SOAP WSDL (&lt;a href="https://orbitalhq.com/docs/describing-data-sources/soap"&gt;here's&lt;/a&gt; the relevant docs)&lt;/li&gt;
&lt;li&gt;Using &lt;a href="https://orbitalhq.com/docs/querying/writing-queries"&gt;Taxi queries&lt;/a&gt; to build the responses we want to return&lt;/li&gt;
&lt;li&gt;Publishing those queries as REST APIs, with LIVE RELOAD FTW!&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The code for this blog is available on &lt;a href="https://github.com/orbitalapi/demos/tree/main/soap-demo"&gt;Github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you find this tutorial useful, why not give us a star on &lt;a href="https://github.com/orbitalapi/orbital"&gt;Github&lt;/a&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  Adding metadata to SOAP for fun &amp;amp; profit.
&lt;/h3&gt;

&lt;p&gt;Taxi metadata allows us to sprinkle a few additional tags into our WSDL which let mark data attributes that are the same.&lt;/p&gt;

&lt;p&gt;Later we can use these tags to automate all the SOAP interaction, so we don't have to write any SOAP code ourselves.&lt;/p&gt;

&lt;p&gt;For example, our CountryInfo wsdl has a section dedicated to request / response payloads.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;
&amp;lt;!-- Snippet from our WSDL - Request / Response messages for getting a Country Name --&amp;gt;
&lt;span class="gd"&gt;&amp;lt;xs:element name="CountryName"&amp;gt;
&lt;/span&gt;    &amp;lt;xs:complexType&amp;gt;
       &amp;lt;xs:sequence&amp;gt;
          &amp;lt;xs:element name="sCountryISOCode"
                 type="xs:string" 
&lt;span class="gi"&gt;+                taxi:type="com.demo.IsoCountryCode"/&amp;gt;
&lt;/span&gt;       &amp;lt;/xs:sequence&amp;gt;
    &amp;lt;/xs:complexType&amp;gt;
    &amp;lt;/xs:element&amp;gt;
    &amp;lt;xs:element name="CountryNameResponse"&amp;gt;
    &amp;lt;xs:complexType&amp;gt;
       &amp;lt;xs:sequence&amp;gt;
         &amp;lt;xs:element name="CountryNameResult" 
             type="xs:string" 
&lt;span class="gi"&gt;+            taxi:type="com.demo.CountryName"/&amp;gt;
&lt;/span&gt;       &amp;lt;/xs:sequence&amp;gt;
    &amp;lt;/xs:complexType&amp;gt;
&lt;span class="gd"&gt;&amp;lt;/xs:element&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can summarize this by saying:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I can send a &lt;code&gt;CountryName&lt;/code&gt; request containing a &lt;code&gt;IsoCountryCode&lt;/code&gt;, and I'll get back a message containing a &lt;code&gt;CountryName&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Elsewhere, we can also use a &lt;code&gt;IsoCountryCode&lt;/code&gt; to get more information, like a &lt;code&gt;CountryFlagUrl&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;
&amp;lt;xs:element name="CountryFlag"&amp;gt;
  &amp;lt;xs:complexType&amp;gt;
     &amp;lt;xs:sequence&amp;gt;
        &amp;lt;xs:element name="sCountryISOCode"
            type="xs:string"
&lt;span class="gi"&gt;+           taxi:type="com.demo.IsoCountryCode"/&amp;gt;
&lt;/span&gt;     &amp;lt;/xs:sequence&amp;gt;
  &amp;lt;/xs:complexType&amp;gt;
&lt;span class="gd"&gt;&amp;lt;/xs:element&amp;gt;
&amp;lt;xs:element name="CountryFlagResponse"&amp;gt;
&lt;/span&gt;  &amp;lt;xs:complexType&amp;gt;
     &amp;lt;xs:sequence&amp;gt;
        &amp;lt;xs:element name="CountryFlagResult"
                type="xs:string"
&lt;span class="gi"&gt;+               taxi:type="com.demo.CountryFlagUrl"/&amp;gt;
&lt;/span&gt;     &amp;lt;/xs:sequence&amp;gt;
  &amp;lt;/xs:complexType&amp;gt;
&lt;span class="gd"&gt;&amp;lt;/xs:element&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ZBFNyeeh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kav73g662ar8qpe5gdaj.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ZBFNyeeh--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kav73g662ar8qpe5gdaj.gif" alt="Sarah Lynn - That's Too Much, Man" width="498" height="286"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Using Taxi queries to get the data we want
&lt;/h2&gt;

&lt;p&gt;Now that our WSDL is annotated with Taxi, we can use TaxiQL to do all the heavy lifting for us.&lt;/p&gt;

&lt;p&gt;Rather than generating a bunch of Java classes from the WSDL, we can simply write a taxi query:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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
 }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New Zealand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"flag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://www.oorsprong.org/WebSamples.CountryInfo/Flags/New_Zealand.jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
   &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New Zealand Dollars"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Orbital's profiler shows us exactly what happened:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--hH54YrxL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4q2i9cycf2o838j8ruw8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hH54YrxL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4q2i9cycf2o838j8ruw8.png" alt="Oribtal Profiler" width="800" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can see:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Orbital called 3 different SOAP endpoints&lt;/li&gt;
&lt;li&gt;It dealt with all the SOAP Request/Response malarchy&lt;/li&gt;
&lt;li&gt;It plucked the data we want, and gave it back.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And we can drill into the details of each call too:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WXA5UHwx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gqpuowrtyibspn9m91gd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WXA5UHwx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gqpuowrtyibspn9m91gd.png" alt="Call details" width="800" height="634"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Instant REST API
&lt;/h2&gt;

&lt;p&gt;Now that we're happy with the content of our payload, we can quickly turn it into a REST API.&lt;/p&gt;

&lt;p&gt;Simply by checking a query into our project's git repo, and adding an annotation, Orbital gives us a fully working REST API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
@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
   }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@HttpOperation(url = '/api/q/countrydata/{countryCode}', method = 'GET')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So, if we call:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl http://localhost:9022/api/q/countrydata/NZ 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Gives us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New Zealand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"flag"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http://www.oorsprong.org/WebSamples.CountryInfo/Flags/New_Zealand.jpg"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"New Zealand Dollars"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"capital"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Wellington"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Live reload for the win
&lt;/h2&gt;

&lt;p&gt;Orbital is live reloading this query, so while we're working locally and making changes to our query, changes are instantly deployed!&lt;/p&gt;

&lt;p&gt;Once deployed, making changes is as simple as pushing to a git repository.&lt;/p&gt;

&lt;p&gt;Check it out:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SePblGro--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xlmdcmz8dauc2jsp8pxe.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SePblGro--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_66%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xlmdcmz8dauc2jsp8pxe.gif" alt="Live reload" width="639" height="674"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Adding the &lt;code&gt;CapitalCityName&lt;/code&gt; into our request actually added a whole new SOAP request and integration into our query, which Orbital instantly reloaded behind the scenes.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;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 &lt;br&gt;
normally comes from dealing with SOAP requests.&lt;/p&gt;

&lt;p&gt;In the process, we've touched on a few of Orbital's handy features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Using Taxi metadata in SOAP WSDLs&lt;/li&gt;
&lt;li&gt;Composing together multiple SOAP calls&lt;/li&gt;
&lt;li&gt;Instant REST APIs with live reload&lt;/li&gt;
&lt;li&gt;Viewing profiling data &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The full code to run this demo locally is available on &lt;a href="https://github.com/orbitalapi/demos/tree/main/soap-demo"&gt;Github&lt;/a&gt;&lt;/p&gt;

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




&lt;h3&gt;
  
  
  Give us star!
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--iM0c1qHT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ls1d8zm26pvbxat92j6w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--iM0c1qHT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ls1d8zm26pvbxat92j6w.png" alt="A small boy asking for a star" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you found this useful, please give our &lt;a href="https://github.com/orbitalapi/orbital"&gt;Github repo&lt;/a&gt; a star!&lt;/p&gt;

</description>
      <category>api</category>
      <category>tutorial</category>
      <category>microservices</category>
      <category>data</category>
    </item>
    <item>
      <title>Semantic Metadata 101: Towards Automated API Orchestration</title>
      <dc:creator>Marty Pitt</dc:creator>
      <pubDate>Tue, 23 May 2023 11:29:09 +0000</pubDate>
      <link>https://dev.to/martypitt/semantic-metadata-101-towards-automated-api-orchestration-1k73</link>
      <guid>https://dev.to/martypitt/semantic-metadata-101-towards-automated-api-orchestration-1k73</guid>
      <description>&lt;p&gt;This post is a lightweight introduction to the concept of Semantic Metadata, and how it makes our enterprise services&lt;br&gt;
automatically composable.&lt;/p&gt;
&lt;h2&gt;
  
  
  What is Semantic Metadata?
&lt;/h2&gt;

&lt;p&gt;Semantic Metadata is a way of defining a contract around the meaning of data.  It lets teams create terms and definitions they agree on, and use those terms to better describe their APIs and analytics, and make software interoperable.&lt;/p&gt;

&lt;p&gt;Teams can use Semantic metadata to define formal definitions for fields in APIs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"This is what a first name means",&lt;/li&gt;
&lt;li&gt;"This is what a company name means"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Analytical platforms such as &lt;a href="https://cube.dev"&gt;cube.dev&lt;/a&gt; tend to expand this to include shared definitions of aggregates.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; "This is what a customer is"&lt;/li&gt;
&lt;li&gt; "This is what we mean by 'Active Customers'", etc.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The core idea is that Semantic Metadata is a way for teams to create a shared understanding of what data means, independent of a single specific system.&lt;/p&gt;
&lt;h2&gt;
  
  
  Building a Taxonomy
&lt;/h2&gt;

&lt;p&gt;Semantic metadata is really just a collection of terms that describe our business.&lt;/p&gt;

&lt;p&gt;When grouped together, this is called a Taxonomy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--I-YSmyFA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e71v8kf04cvfughxhn9w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--I-YSmyFA--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/e71v8kf04cvfughxhn9w.png" alt="An example taxonomy" width="800" height="493"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://taxilang.org"&gt;Taxi&lt;/a&gt; is a language-agnostic tool for building semantic taxonomies:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type AccountNumber inherits Int
type CreditScore inherits Decimal
type FirstName inherits String
// etc...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Semantic Metadata is designed to be shared across multiple teams.  So, just like with API technologies like OpenAPI, Protobuf, etc - it's best to have Semantic Metadata designed in a platform agnostic language.&lt;/p&gt;

&lt;p&gt;Generators can then generate bindings / SDKs / tools as required in whichever technology consuming teams are working with.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--abyeRO8z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9iec114b8epzl2kesh1z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--abyeRO8z--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9iec114b8epzl2kesh1z.png" alt="Using Generators with a Taxonomy" width="800" height="582"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Embedding semantic metadata
&lt;/h2&gt;

&lt;p&gt;On it's own, semantic metadata isn't very helpful - it's just a set of tags and definitions.&lt;/p&gt;

&lt;p&gt;However, embedded in API specs, it becomes much more powerful.&lt;/p&gt;

&lt;p&gt;Here's an example in OpenAPI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;openapi&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3.0.1&lt;/span&gt;
&lt;span class="na"&gt;info&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ReviewsApi&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.0.0&lt;/span&gt;
&lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;https://reviews/{id}:&lt;/span&gt;
    &lt;span class="s"&gt;get&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;parameters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;id&lt;/span&gt;
        &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;path&lt;/span&gt;
        &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
        &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;x-taxi-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FilmId&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- Semantic metadata&lt;/span&gt;
      &lt;span class="na"&gt;responses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;200"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;application/json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;array&lt;/span&gt;
                &lt;span class="na"&gt;items&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;$ref&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;#/components/schemas/FilmReview'&lt;/span&gt;
&lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;schemas&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;FilmReview&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;object&lt;/span&gt;
      &lt;span class="na"&gt;properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;x-taxi-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ReviewId&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- Semantic metadata&lt;/span&gt;
        &lt;span class="na"&gt;filmId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;string&lt;/span&gt;
          &lt;span class="na"&gt;x-taxi-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;FilmId&lt;/span&gt;  &lt;span class="c1"&gt;# &amp;lt;-- Semantic metadata&lt;/span&gt;
        &lt;span class="na"&gt;score&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;integer&lt;/span&gt;
          &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;int32&lt;/span&gt;
          &lt;span class="na"&gt;x-taxi-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ReviewScore&lt;/span&gt; &lt;span class="c1"&gt;# &amp;lt;-- Semantic metadata&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As teams enrich their exisitng API specs with semantic metadata, tooling can start inferring relationships between APIs and data sources.&lt;/p&gt;

&lt;p&gt;Let's look at a (very) simplifed API for an insurance company that provides quotes:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lIZw2CdC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fc8vfo10zdr7md6ajyax.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lIZw2CdC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fc8vfo10zdr7md6ajyax.png" alt="An example of a GetQuote function" width="800" height="164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This takes a request payload with two inputs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
   "noClaimsBonus" : 0.25,
   "creditScore"  : "AAA"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Semantically, this can be modelled as:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;type NoClaimsBonus inherits Decimal
type CreditScore inherits String

model QuoteRequest {
  noClaimsBonus : NoClaimsBonus
  creditScore : CreditScore
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This has added a small benefit of improved clarity in the docs.&lt;/p&gt;

&lt;p&gt;However, the real payoff is in when we're trying to get out services to work together...&lt;/p&gt;

&lt;h2&gt;
  
  
  The Payoff: Automating Interoperability
&lt;/h2&gt;

&lt;p&gt;This is where semantic metadata really starts to shine.&lt;/p&gt;

&lt;p&gt;With Semantic Metadata embedded in our API specs, we can start to infer relationships between APIs and data.&lt;/p&gt;

&lt;p&gt;Looking at our previous example without field names, we required two inputs - a &lt;code&gt;NoClaimsBonus&lt;/code&gt; and a &lt;code&gt;CreditScore&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--WcP_TLvV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b2l370cwb55is44crxbr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--WcP_TLvV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b2l370cwb55is44crxbr.png" alt="An example of a GetQuote function without field names, using semantic metadata" width="800" height="198"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we don't have those pieces of information, we need to look them up.  Which means we need to look for services that expose this data.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AanpJ_qW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4g5zn8h5vj88va2cry5s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AanpJ_qW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4g5zn8h5vj88va2cry5s.png" alt="Semantic Metadata applied to other functions" width="800" height="210"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As our APIs are enriched with Semantic Metadata, tooling can automatically infer relationships between systems.&lt;/p&gt;

&lt;p&gt;So, in our Insurance Quote example, while it's unlikely we have either a &lt;code&gt;NoClaimsBonus&lt;/code&gt; or &lt;code&gt;CreditScore&lt;/code&gt; available from our UI, we might have something else - like a &lt;code&gt;UserName&lt;/code&gt; or &lt;code&gt;UserId&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Semantic Metadata lets us use tooling to automate the integration, linking from the Things We Know (&lt;code&gt;UserName&lt;/code&gt;) to the Things We Want To Find Out (a &lt;code&gt;Quote&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;We'll look into this in more detail in the next post.&lt;/p&gt;

&lt;h2&gt;
  
  
  A few tips &amp;amp; tricks...
&lt;/h2&gt;

&lt;p&gt;Semantic metadata is a simple concept, and is super easy to get going with.  Here are a few tips &amp;amp; tricks to take along your journey&lt;/p&gt;

&lt;h3&gt;
  
  
  Stay small &amp;amp; nimble when using distributed ownership
&lt;/h3&gt;

&lt;p&gt;Semantic Metadata is intended for wide collaboration, which can be tricky.&lt;/p&gt;

&lt;p&gt;Where individual teams are responsible for defining API definitions, semantic metadata has distributed ownership.&lt;/p&gt;

&lt;p&gt;If the idea of Distributed Ownership is giving you sweaty palms and flashbacks to Design-by-committee meetings&lt;br&gt;
when your org tried to implement &lt;a href="https://teivah.medium.com/why-is-a-canonical-data-model-an-anti-pattern-441b5c4cbff8"&gt;Canonical Domain Models&lt;/a&gt;, you might already be rolling your eyes.&lt;/p&gt;

&lt;p&gt;Therefore, it's &lt;a href="https://orbitalhq.com/blog/2023-01-16-using-semantic-metadata#defining-a-semantic-type"&gt;recommended&lt;/a&gt; that Semantic Metadata is defined on Scalar terms only - single noun-like ideas that describe exactly one idea. eg:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;FirstName&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;LastName&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DateOfBirth&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;PostCode&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are relatively un-contentious to define, and - unlike domain models (which evolve as systems mature) - semantics don't really change.&lt;/p&gt;

&lt;h3&gt;
  
  
  GitOps all the things
&lt;/h3&gt;

&lt;p&gt;Semantics are a bridge between business language and software.  There's no shortage of First-generation data catalog platforms that will sell you a glorified Wiki for defining your semantic terms.&lt;/p&gt;

&lt;p&gt;Instead, consider using Open Source tooling, that aligns with GitOps.&lt;/p&gt;

&lt;p&gt;You get peer review, audit trails, and automated workflows through all your existing Git tooling, without having to invest in expensive enterprise tooling.&lt;/p&gt;

&lt;h3&gt;
  
  
  Staying technology agnostic
&lt;/h3&gt;

&lt;p&gt;Organisations these days aren't "Java Shops" or ".NET Shops" anymore - they're polyglot, with tech teams&lt;br&gt;
choosing the tech stack that best fits the task and team.&lt;/p&gt;

&lt;p&gt;Likewise, most API and schema technologies (eg., OpenAPI, Protobuf, etc) are language agnostic, with bindings / generators&lt;br&gt;
allowing them to be consumed in whichever technology consuming teams are working with.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/taxilang/taxilang"&gt;Taxi&lt;/a&gt; is an example of a Semantic Metadata language, which is also platform-agnostic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;This has been a high level introduction into some of the ideas behind Semantic Metadata.&lt;/p&gt;

&lt;p&gt;To go deeper, take a read into &lt;a href=""&gt;Why we created Taxi&lt;/a&gt;, and &lt;a href="https://orbitalhq.com/blog/2023-01-16-using-semantic-metadata"&gt;Using Semantic Metadata for easier Integration&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the next article, we'll take a look at building a simple application using Semantic Metadata to automate the orchestration.&lt;/p&gt;

&lt;p&gt;Also, if you liked this article, consider giving &lt;a href="https://github.com/taxilang/taxilang"&gt;Taxi a star&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://GitHub.com/taxilang/taxilang/stargazers/"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ihSEm_iL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://img.shields.io/github/stars/taxilang/taxilang.svg%3Fstyle%3Dsocial%26label%3DStar%26maxAge%3D2592000" alt="GitHub stars" width="76" height="20"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>api</category>
      <category>programming</category>
      <category>architecture</category>
    </item>
  </channel>
</rss>
