Web API Documentation with OpenAPI

Documentation is great. Creating good docs and keeping them up to date is hard and very important.

Even if your APIs are for “internal” consumption only - it still pays off to document them properly, because one day the API authors are the only clients of that API, but one week later three different teams are using it. The API has grown, but you’re still good, because this is why you’ve created two separate apps - backend and frontend in the first place. Two weeks later your original service becomes a client of two new services that have joined your system, both maintained by different people. And it’s great because there are awesome docs available for them, you can iterate faster and everyone is happy. Three weeks later you’re releasing a new version of the original API and it’s all good because you have both old and new versions documented!

This sounds like development utopia. Maintaining a system like that is not easy. Keeping it up while rolling out new features and re-architecting parts of it is hard enough with accurate docs being available. Fortunately, there are “only” so many ways in which an HTTP based API can behave and there are tools available to encode these rules using agreed upon formats. One of them is the OpenAPI Specification, based on version 2.0 of Swagger.

This post aims to very briefly introduce the specification, just enough to interest you in checking it out and gives a quick overview of available tooling around it that can be used to create specification files and produce documentation.

Quick Introduction to the Specification

So let’s say your API has one endpoint https://example.com/keyboard/. It can be used to create new keyboards by sending it a POST request with application/json body and a JWT authorization header. An example successful request and response could look like this:

Request:

POST /keyboard/ HTTP/1.1
Host: example.com
Content-Type: application/json
Content-Length: 88
Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleGFtcGxlIjoxfQ.Q9HCpsH4T6GkbOXmjJLvYlBxkGxg8r08aBjlBqtHMH4

{
    "name": "Hello kbd",
    "keys": ["Hi", "Hello", "Bye"],
    "backlight": false
}

Response:

HTTP/1.1 201 CREATED
Content-Type: application/json
Content-Length: 134

{
    "id": "3d19109322a146dbb79519753ccd7dae",
    "name": "Hello kbd",
    "keys": ["Hi", "Hello", "Bye"],
    "backlight": false
}

This endpoint can also respond with HTTP 400, when the input is of invalid format and with HTTP 401 when authorization fails. Using Swagger specification we can encode all of this in a YAML file:

swagger: '2.0'
info:
  version: 1.2.3
  title: Keyboards API
  description: Creates beautiful keyboards
schemes:
  - https
security:
  - JWTAuthToken: []
consumes:
  - application/json
produces:
  - application/json
paths:
  /keyboard/:
    post:
      description: Creates a new keyboard
      operationId: keyboard.create
      parameters:
        - name: KeyboardInputData
          in: body
          required: true
          schema:
            $ref: '#/definitions/NewKeyboardInputData'
      responses:
        '201':
          description: Details of the newly created keyboard
          schema:
            $ref: '#/definitions/KeyboardData'
        '400':
          description: Invalid input error
          schema:
            $ref: '#/definitions/InvalidInputError'
        '401':
          description: Unauthorized

securityDefinitions:
  JWTAuthToken:
    type: apiKey
    name: Authorization
    in: header
    description: |
      All endpoints expect a JWT `Authorization` header. The format of
      the header is `JWT <token>`.

      Example:

          Authorization: JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleGFtcGxlIjoxfQ.Q9HCpsH4T6GkbOXmjJLvYlBxkGxg8r08aBjlBqtHMH4

definitions:
  KeyboardData:
    type: object
    required:
      - id
      - name
      - keys
      - backlight
    properties:
      id:
        type: string
        pattern: '^[a-f0-9]{32}$'
      name:
        type: string
      keys:
        type: array
        items:
          type: string
        uniqueItems: true
      backlight:
        type: boolean
  NewKeyboardInputData:
    type: object
    required:
      - name
      - keys
    properties:
      name:
        type: string
      keys:
        type: array
        items:
          type: string
        uniqueItems: true
      backlight:
        type: boolean
  InvalidInputError:
    type: object
    properties:
      code:
        type: string

Some of the the concepts probably look self explanatory, but everything is well documented in the specification. We managed to specify:

Tools

Swagger Editor is very useful for creating the spec. One of the best features is that it renders a very nice looking HTML preview of the documentation right next to the spec as you type. There’s an official docker image available to run the editor locally. Unfortunately, at the time of writing this, Swagger Editor does not support exporting of the HTML preview as something that can be used as standalone documentation (see this issue for details).

The official documentation rendering tool - Swagger UI unfortunately by default won’t produce as nice looking documentation as the preview in Swagger Editor. However it can be customized.

Swagger Codegen is a tool for generating API clients/servers based on a spec and it can generate html and dynamic-html clients” (basically API docs) but they are not as well organized and good looking as what Swagger UI can produce.

Both Swagger UI and Swagger Editor docs don’t seem to offer an “export” option that would just create static files that can be easily hosted. Swagger UI is a Node.js application - via browser a user points it at a spec file and it validates and renders docs dynamically.

Both Swagger UI and Swagger Editor generated docs offer a way of interacting with the API by providing forms that can be used to make requests against the live API.

If you’re looking for easily hostable docs, bootpring-openapi may be interesting (It’s mentioned in the Swagger Editor issue). This tool can be used to generate clear looking API docs as one HTML file (with CSS inlined).

Summary

API Docs are important, especially in multi service and multi team environments. OpenAPI Specification is a powerful tool for describing web APIs. There are others (one of them is RAML). I’ve found OpenAPI spec useful and comprehensive enough for my needs. There are many handy tools for working with it, although it would be great if the default docs generated by Swagger UI were more like the ones previewed in Swagger Editor. If you think there’s anything I’ve missed or you have interesting experiences to share in the API spec area, please get in touch!

Thanks to Kristian Glass for reviewing this.