Using API Description at EVRYTHNG

Introduction

When dealing with a complex API with many different possible types of request, it can be difficult to know exactly how to interact with it. For example, what is the correct input data for a given request? What data can I expect to receive in a given response? Being certain about these pieces of information are what separate a reliable enterprise API from a two-day hackathon project! Serious consumers require reliability both in surity of knowledge as well as uptime of the product itself.

The usual path a service follows when inviting others to consume their API to send and receive data is to write written prose documentation about how to use the service’s API. Any software developer wishing to know how to interact with it will seek out and consult the provided documentation, and hopefully learn enough to be able to use it effectively to achieve their goals and build a useful integration.

As an example, at EVRYTHNG we have two main repositories of knowledge and wisdom:

  • Documentation - the general API concepts, walkthroughs, and tutorials.
  • API Reference - the precise, structured information on how to use the API itself.

Traditional Documentation

The problem with writing and maintaining complex documentation manually is that it is not a trivial task to convey all the required knowledge and concepts across to the reader in a universally accessible format, especially when the authors may have been so involved with the development of the API that they struggle to see the needs of someone who is not as expert, and so meet their needs as a consumer.

The end result is that the consuming developer must make do with whatever they are provided with - and poorly written, incomplete, incorrect, or out of date documentation can cause a potential customer to be put off and seek an alternative product for their needs. Not good for us, obviously!

Therefore if an API is badly documented, both the owner and consumer of that API suffer and will not be able to realise its full potential. And even if great effort is put into the API documentation to begin with, over time it is often neglected, may go out of date, and is not guaranteed to be kept fresh and reliable when called upon.


A More Modern Approach

After creating and maintaining up-to-date API documentation in the traditional sense, we became aware of some new technologies that can be used to alleviate a lot of the inherent difficulties in effectively informing our developers how to use the EVRYTHNG API. These new approaches use standardised machine-readable data and automation tools to help take away the uncertainty and lack of clarity or structure that poorly written or neglected documentation can cause.

Swagger - Machine-readable Documentation

First up in this blog post is Swagger (also known as OpenAPI), a special extension of the JSON Schema specification that provides a powerful and predictable way to describe all the intimate details of how an API works. This includes all the applicable API endpoints, methods, parameters, error messages, and the associated request and response payloads in the form of JSON Schema objects.

By virtue of its format, a Swagger specification file is readable by both humans and machines, and can be used in many ways, as the non-exhaustive list below shows:

  • Generating structured documentation.
  • Providing automatic API testing and validation.
  • Generation of example SDKs and client/server implementations.
  • Adding intelligence about the API to other tools, such as CLIs.
  • A solid contract that developers can rely upon.

So, one file can bring a lot of value. What does it look like?

Without going into too much detail, here is a brief sample from the EVRYTHNG Swagger file (available on GitHub), describing the API operation for creating a Thng. If you are not familiar with this operation, consider reading the equivalent traditional written documentation in our Developer Hub first.

"/thngs": {
  "post": {
    "tags": [ "Thngs" ],
    "summary": "Create a Thng",
    "description": "Create a new Thng.",
    "parameters": [
      { "$ref": "#/parameters/ThngDocumentCreateBody" }
    ],
    "responses": {
      "201": {
        "description": "The Thng was created successfully.",
        "schema": { "$ref": "#/definitions/ThngDocument" },
        "examples": {
          "application/json": {
            "id": "UFsyXmXCVq8NtrRwREMfGaSs",
            "createdAt": 1491233125432,
            "updatedAt": 1491233125432,
            "name": "TestThng"
          }
        }
      }
    },
    "x-api-keys": [ "Operator", "Trusted Application", "Application User" ]
  }
}

Let’s break it down to see what each part describes:

  • /thngs - the API endpoint or path to the relevant resource.
  • post - the HTTP method, in this case for sending data to the API.
  • tags, summary, description - useful for generating documentation.
  • parameters - refers to the required input parameters.
  • responses - the expected responses to this type of request.
    • 201 - the expected response HTTP code.
    • description - useful for generating documentation.
    • schema - a standalone schema describing the expected response format.
    • examples - an example response payload, also used in documentation.
  • x-api-keys - an EVRYTHNG-specific ‘vendor extension’ allowing us to describe which API keys are allows to make this request.

Using all these pieces of information, a developer or other automated client knows exactly what is accepted as valid input, and knows what they can expect to receive as output. Every kind of operation available in the API follows this same pattern, with different values as appropriate. For example, in order to read products, the path would be /products and the method would be get.

As an example, using the SwaggerUI tool we can generate dynamic documentation with a built-in testing capability for this request:

The value to us as a company is that we can keep all the information about how the EVRYTHNG API is supposed to work in one single place under version control, enabling collaborative updates to it, and using it to generate documentation samples and create automated tests. We can alsoshare it publicly, allowing virtually limitless other tools, integrations, documentation, and tests to be built by our community of developers.

Which brings us to the next technology for improving documentation.


## Dredd - Automated API Validation

As a consumer of a Swagger description file, Dredd performs automated tests that use the request and response schemas to create and send valid API requests. Once a response is received, it will validate the response payload against what is expected in the description file using the appropriate schema.

This capability is useful twofold:

  • Every single API operation can be tested and validated to be working as expected (by examining the response and associated side effects, such as resources created).
  • If the request documented in the Swagger API description file caused an error from the API, or the response does not match the documented schema, the test will fail and we will know that we have unexpectedly introduced a change in the API (which unchecked could have severe consequences for our consumers).

Let’s look at an example of this in action. The snippet below (edited for brevity) shows the five main operations available through the EVRYTHNG API for working with Thng resources.

"/thngs": {
  "post": {
    "summary": "Create a Thng",
  },
  "get": {
    "summary": "Read all Thngs",
  }
},
"/thngs/{thngId}": {
  "get": {
    "summary": "Read a Thng",
  },
  "put": {
    "summary": "Update a Thng",
  },
  "delete": {
    "summary": "Delete a Thng",
  }
}

When put to the test against the real API by Dredd, the output is five passing tests (out of a current total of 167 operations!)

If the API description is modified (perhaps through some misunderstanding) to expect ‘Create a Thng’ to return an object with friendlyName instead of name, the test fails and we learn what changed:

Dredd is telling us three things here:

  1. The test for ‘Create a Thng’ failed.
  2. The field friendlyName expected in the schema was not present in the response.
  3. The API actually returned name instead of friendlyName, but extra fields not in the API description are not allowed, so it caused the test to fail.

We can use this information to track down where the change was introduced, and fix the problem. By running tests using Dredd on a regular basis and before releases, we can be more confident that we are not introducing any unexpected breaking changes that could cause our customers’ integrations to break as well. Day saved!


## Wrapping Up

In conclusion, we’ve found that the initial upfront time and effort cost of creating the EVRYTHNG API Swagger description and setting up Dredd testing to be worth the time, simply for the confidence and bug-finding value it provides right now, but also for much more:

  • Fast generation of structured API documentation snippets.
  • Automated API request and response validation.
  • Early detection of unintended API changes, as well as subtle behavior changes outside of the syntactical in testing as well as in production, allowing early detection of future problems that may be about to occur.
  • Use to add intelligence about the API to other tools and automated services.

In the future, we will use it in even more valuable ways, and that’s without all the potential cool and useful things that third-party developers can build using the API description as a base to expand upon.

If you’re interested in learning more about Swagger, Dredd, as well as getting your hands on our API description and building something new, here are some links to get you started: