APIMatic Review: Developer Experience-as-a-Service

APIMatic Review: Developer Experience-as-a-Service

This article is sponsored by APIMatic, in that they're paying for our time to review their product, but they're not paying for what we write. If it's rubbish, we'll let you know.

I've used APIMatic a fair bit in the past, but never had the time to write about it. I'd like to thank APIMatic for paying for me to sit down and see what they've been up to lately, so I can write it up, and you can see if it will fit into your API toolkit.

Since I last used APImatic a few years back they've released v3.0 with a bunch of new features and improvements.

  • Overhauled Code Generator: Support for latest language versions, new immutable client design, timeout and retries, XML, deprecated methods, enhanced OpenAPI support, and more.
  • Redesigned API Portal: A new slick look, new API Explorer, language-specific guides, pop-out code samples, and more.
  • New Docs Editor: Create pages faster with our new Markdown editor and organize your content the way you want.
  • DX Analytics: Analyze the performance of your API through detailed charts per language for metrics like API Portal page visits, SDK downloads, Time to first Hello World and much more.

Noice! Let's jump in.

Getting Started

Once I'm done signing up for an account the UI asks me for an API description, like an OpenAPI document or a Postman Collection.

My OpenAPI usually lives in a Github repo somewhere, but this is more of an "Import to APIMatic" process than a "Tell us where it lives" kinda process.

That's a little tricky, because my OpenAPI is usually split up into multiple reusable files with $ref, and it was asking for "A File".

For a moment I thought I'd need to bundle it up into a single file ($ swagger-cli bundle api/openapi.yaml > /tmp/bundled.json), but before going down that route I took a punt at zipping up the folder which contained openapi.yaml and schemas/*.yaml`, and throwing that into the upload form.

Phew, it worked!

Immediately I was rewarded with API reference documentation for the Protect Earth API, with a built in request samples, response examples, and API Playground.

I'm also seeing all sorts of buttons about making an SDK, without having to muck about with CLI tools.

Where to start!

SDKs (Software Development Kits)

I'll start with the SDKs, because it's one of the more unique offerings. All the popular "API management" tooling vendors like Postman, Stoplight, Kong, Optic, Mulesoft, etc. cover an over-lapping combination of verticals, like API design, mocks, docs, testing, learning, playground, etc., but so far as I know none of them offer to build an SDK for you.

If you're new to SDKs here's an excerpt from the new Surviving Other People's APIs book (aimed at API consumers) to get you caught up.

Knowing how to interact with a HTTP API directly is a good skill to have, but sometimes the API team will have created a library/package/module written in your programming language of choice, so that you don't have to bother. This is know as a Software Development Kit, or an SDK for short.
Theoretically, an SDK abstracts most of the transport layer specifics away behind idiomatic code, taking care of authentication, URLs, methods, headers, data formats, caching, retries, even HTTP status codes will be converted into meaningful exceptions. The best SDKs will even turn validation errors into a useful format. If that is the case for the specific API you are attempting to integrate with, then you might be in luck, and perhaps you can skip a lot of this book.
For example, we have not covered how Authentication works yet, and with a good SDK you would not need to know. They will give you somewhere to pop in the authentication details, in this instance an API key.
import Stripe from 'stripe';

const stripeApiKey = 'sk_test_4eC39HqLyjWDarjtT1zdp7dc';

const stripe = new Stripe(stripeApiKey);
const charge = await stripe.charges.create({
  amount: 2000,
  currency: 'usd',
  source: 'tok_amex',
  description: 'My First Test Charge',
});

Developers consuming an API either love 'em or hate 'em, but you don't need everyone to be excited about using an SDK, you just want to make sure developers trying to interact with your API have the choice available to them.

In my experience it's a huge faff to generate them. You have to make some sort of CI/CD pipeline that notices changes to OpenAPI descriptions, rebuilds the SDK with hopefully some sort of breaking change detection, publishes new versions, updates docs, etc.

That's all possible but I think most of us would rather be working on the product instead of mucking around with "DocOps" like this, so APIMatic aims to do that for you.

Generating SDKs with APIMatic

APIMatic lets you make SDKs, and there's various ways to do this automatically but you can just download them ad-hoc to see how they look.

When you click download ZIP there's the option to download .Net, Java, PHP, Python, Ruby, or TypeScript, which is a pretty decent spread of options.

I'll go with TypeScript because it's going to be familiar to the biggest cross section of you all.

There's a whole folder structure made, full of code! That was infinitely easier than mucking about with setting up Java to try and use OpenAPI Generator.

Before I dig into the code, a quick look at the generated README for my new SDK. It's super verbose, and spends a lot of time telling you how to make sure npm is set up, which the average TypeScript user probably knows. I'd have to trim this down quite a lot, and replace a few paragraphs with a single npm -S install @your-org/sdk, which is something I'd have to potentially automate.

The generated SDK has a good load of options, including timeouts and retries, which is great news as they're super important and often overlooked!

Parameter Type Description
timeout number Timeout in milliseconds.
httpAgent any Custom http agent to be used when performing http requests.
httpsAgent any Custom https agent to be used when performing http requests.
retryConfig Partial<RetryConfiguration> Configurations to retry requests.

There's another option that I was a bit surprised to see:

Parameter Type Description
accessToken string The OAuth 2.0 Access Token to use for API requests.

I am glad that the SDK generator can support accessToken for OAuth 2.0, but... the Protect Earth API doesn't! 😅

I'll ignore that as its probably optional, and I can remove it from my tweaked README and more human documentation later, but I feel like that should be based on the OpenAPI securitySchemes and only show up if there's a type: oauth2 present.

SDK Documentation

The SDK is documented in a few ways. You can look around the hosted documentation online on APIMatic, or you can look at the README.md and subsequent doc/controllers/*.md files.

To find out how to make a particular request can be a little tricky as you have to 1) look at how to do instantiation in the README, 2) see how to work with the controller, then 3) find the example of the method name. Took me a minute to piece it all together but once you know where to look it's ok.

The way APIMatic generates the SDK is by turning top-level URL segments into controllers, so you get a MapController class for the maps service, and creates methods based on the OpenAPI operationId.

'/maps/sites':
    get:
      operationId: get-project-pins
  
'/maps/sites/{uuid}':
    get:
      operationId: get-tree-pins

These two endpoints then become:

const mapController = new MapController(client);

mapController.getProjectPins();
mapController.getTreePins(uuid);

If you pop all the bits of code examples together then it looks a bit like this:

import { ApiError, Client, MapController } from 'protect-earth-apilib';

const client = new Client({
  timeout: 0,
});

const mapController = new MapController(client);

const uuid = '61f423e5-3cac-4e82-a520-b233730637bb';
try {
  const { result, ...httpResponse } = await mapController.getTreePins(uuid);
  console.log({ result, httpResponse });

} catch(error) {
  if (error instanceof ApiError) {
    console.log('api error', error.result);    
  } else {
      console.log('unknown error', error);
  }
}

That looks pretty reasonable to me. Let's "install it" and give it a try.

npm install -S "/Users/phil/Downloads/Protect Earth API-TS_GENERIC_LIB"

That'll add this to my package.json dependencies:

  "dependencies": {
    "protect-earth-apilib": "file:../../Downloads/Protect Earth API-TS_GENERIC_LIB"
  }

I know that looks a bit funny but its because it's a ZIP sat on my hard drive. Just pretend you've published it off to NPM, and go along with it.

$ node index.js
api error [Object: null prototype] {
  title: 'Page not found',
  type: 'https://protect.earth/probs/page-not-found',
  detail: 'Could not find anything for the provided URL.',
  instance: 'maps/sites/61f423e5-3cac-4e82-a520-b233730637bb'
}

Ah yeah fair cop. You got me. That UUID is nonsense. Let's update that and run it again.

{
  result: {
    type: 'FeatureCollection',
    features: [
      [Object], [Object], [Object], [Object], [Object], [Object],
      [Object], [Object], [Object], [Object], [Object], [Object],
      [Object], [Object], [Object], [Object], [Object], [Object],
      [Object], [Object], [Object], [Object], [Object], [Object],
      [Object], [Object], [Object], [Object], [Object], [Object],
      [Object], [Object], [Object], [Object], [Object], [Object],
      [Object], [Object], [Object], [Object], [Object], [Object],
      [Object], [Object], [Object], [Object], [Object], [Object],
      [Object], [Object], [Object], [Object], [Object], [Object],
      [Object], [Object], [Object], [Object], [Object], [Object],
      [Object], [Object], [Object], [Object], [Object], [Object],
      [Object], [Object], [Object], [Object], [Object], [Object],
      [Object], [Object], [Object], [Object], [Object], [Object],
      [Object], [Object], [Object], [Object], [Object], [Object],
      [Object], [Object], [Object], [Object], [Object], [Object],
      [Object], [Object], [Object], [Object], [Object], [Object],
      [Object], [Object], [Object], [Object],
      ... 5600 more items
    ]
  },
  httpResponse: {
    body: '{"type":"FeatureCollection","features":[{"type":"Feature","geometry":{"type":"Point","coordinates":[-0.27824000000000004,51.965559999999996]}

Huzzah!

Thankfully it all worked even without me passing me a accessToken: 'AccessToken',, because I... don't have one. 😅

The docs might be a little different to how I'd write them, but it's made some pretty useful code for me, and I can always publish my own README.md over the top of theirs. The most important thing is having all that handily written code!

When you're ready to publish it, you can have it push to GitHub and use some CI/CD shenanigans, like using npx semantic-release to automate releases to NPM, or you can use the built in package management support for Ruby Gems or PyPy. Whatever package manager you use, you won't have to download Zip files or do that funny looking local filesystem install method I did just to play around.

SDKs are looking pretty good to me. Let's look at what else they're offering.

API Reference Documentation

There are a lot of options for API reference documentation, so I will try and pop them all on a venn diagram to chart what I think the priorities were as the teams put the tools together, based on some guesswork, nattering with the teams, and the fact I worked on Stoplight Elements as a Product Manager in the past, and had to cut a lot of stuff from the release. 😆

To me, APIMAtic might not be the prettiest API ref docs tool around, but it's got some interesting functionality which makes up for that.

One of the first features I noticed was the integration of the SDK into the documentation APIMatic generates. Users can change the dropdown from HTTP to the language of their choice and see the code samples in different languages.

Most documentation tools have language selectors to change the sample code, but its usually either a generic HTTP client in that language, like Guzzle for PHP or Fetch for JavaScript, but this is your SDK.

Those tools which do support inserting a SDK in do it via OpenAPI extensions that need to be kept up-to-date with some hamster-powered nonsense in your CI/CD pipeline.

  x-codeSamples:
    - lang: 'cURL'
      label: 'CLI'
      source: |
        curl --request POST \
        --url 'https://data.apiexample.com/api/example/batch_query/json?format=json' \
        --header 'content-type: application/octet-stream: ' \
        --data '{}'

Having APIMatic already know how your SDK works means you can skip all of that noise, and just publish your API docs knowing that HTTP, PHP, TypeScript, Ruby, etc is all working out of the box.

The APIMatic Documentation offering covers more than just Ref Docs too, it can handle other Guide-like content, showing off getting started guides, or allowing you to document workflows instead of just throwing users at the ref docs and telling them to piece everything together themselves.

Then there's the dev portal.

Dev Portal

There's a million "API Docs is about more than just API Reference Docs" articles out there so I won't subject you to that. Suffice to say you want to have other guides, getting started docs, help people play around with the API somehow ("API Playground" or "API Console"), and ideally some sort of landing page.

APIMatic are helping out there too. They offer a hosted dev portal which can be embedded in an existing portal, or you can generate and host the docs yourself.

It only took a minute to slap a logo on it, and I could change all the colors, even the favicon, right from the UI without embed anything as a React component or faff around with a CLI.

API Transformer

I have used this tool extensively since 2016 and can tell you it's matured nicely over the years. Being able to convert freely between all these different formats is incredibly powerful.

The most convoluted setup I used it in was a homegrown dev portal pipeline that was taking in API descriptions from a myriad of different teams, including two acquired companies, and trying to turn it all into a single consistent API Catalogue and a Dev Portal. My pipeline was taking in RAML, API Blueprint, and OpenAPI v2, then using APIMatic Transformer we converted it all into OpenAPI v3.0 to build the API documentation (and jankily make SDKs using hand-rolled solution). Then I would ironically convert everything that wasn't already a Postman Collection into a Postman Collection, just so people had OpenAPI-based docs and mocks, and had Postman as an API Playground.

APIMatic does a lot of that for you, so it's worth having a play around to see if you can avoid training 100 hamsters to power your own dodgy DocOps setups, and get back to working on whatever Drone/Crypto/AI startup you're being forced to work on now.