Generating Static Reference Documentation from an OpenAPI Specification

February 2021 ยท 7 minute read

Summary

Background

The OpenAPI Initiative is a fantastic Linux Foundation initiative that maintains a set of standards by which web API’s (Application Programming Interfaces) can be defined by code.

When an API is defined using a standard code template, tooling can be built around that code specification in order to automate processes that allow for a better user experience for that API. One extremely popular tool that was built around the OpenAPI specification is Swagger UI.

Swagger UI is an amazing, well-loved tool in the developer community. It is an open-source project that allows free generation of API endpoint reference documentation, and it has had an enormous positive impact on both internal-facing and external-facing APIs.

Swagger UI has been making life easier for developers for years. I think it is an outstanding product that should be adopted by more development teams.

The Problem with Swagger UI and Static Site Generators

The topic of this post is not to criticize Swagger for what it is, but rather to highlight that many modern API documentation sites are using static site generators to create the site content. Unfortunately, Swagger UI was not designed to work well with these static site generators, because the resulting files from Swagger UI and static site generators are both independent, functional web pages. Combining the two into a single documentation website makes for a more a slower and aesthetically fractured website.

Swagger UI makes well-designed and useful reference documentation, but it is designed to be a standalone product. Developers have tried to embed Swagger in Static-Site Generators. While functional, this solution is messy. Embedding someone else’s standalone product in your own page, and even as it’s own page on your static site, means that you lose design continuity and gain a bloated, harder-to-maintain code base. You end up with tons of extra JavaScript and CSS files that might directly conflict with your own well-designed assets. This results in worse site performance and a discontinuous user experience.

If you’re using a static site generator to embed a standalone, dynamically-rendered third-party product, aren’t you violating the principles that led to the use of a static site generator in the first place?

The Solution

The solution I’ll describe here is specific to the Hugo framework, but can likely be extended for other static site generators based on the built-in tooling available to those frameworks.

If you’re using Hugo for your documentation site, use Hugo’s built-in capabilities to do exactly what Swagger UI does - generate static assets from the OpenAPI Specification file. Huge page performance gains can be realized because Hugo generates this page content during the build, rather than in the user’s browser. I tested both Twitter’s and GitHub’s API definitions and found my example to deliver performance gains of 45-67% as measured by the Google Lighthouse score.

This approach allows you to maintain the same design framework across all of your site, including the API Reference documentation. This also allows you to serve the bare minimum in page assets to individuals that visit your documentation site.

The end result is that you maintain your site design, you dramatically reduce the sizes of assets served, you increase page performance, and your site visitors receive a better overall experience.

Hugo’s Built-In JSON and Data Capabilities

Hugo allows json files to be accessed like a map once the files have been loaded. There are two ways to do this.

  1. This method only works for publicly-available .json definition file formats, which is the industry standard method for fetching JSON Schemas. You can use the built-in Hugo function getJSON, which would load the JSON as a map that you would access like so:
{{ $openApiDefinition := getJSON "https://raw.githubusercontent.com/mycompany/openapi-description/myapi-definition.json" }}
{{ range $openApiDefinition }}
  // all values become accessible via .key notation
  // you can range over additional nested values
{{ end }}
  1. This method works for both .json and .toml definition file formats. You can keep a copy of your OpenAPI specification .json or .toml file in the /data folder itself, where it becomes accessible in templates via the .Site.Data variable. For a schema file at /data/schemas/myapi.toml, you would access it like so:
{{ range $.Site.Data.schemas.myapi }}
  // all values become accessible via .key notation
  // you can range over additional nested values
{{ end }}

Using either of the above methods, the OpenAPI definition file would be parsed during the site build, and static content would be constructed based on the loaded definition. This obviously would require a new build and deployment every time the OpenAPI specification gets updated, but you could easily create a hook to re-build and deploy the relevant documentation pages as part of your API definition update procedure.

Working Example

I coded a working example that can be found on GitHub

Performance Benefits

I tested v0.1 of my working example with OpenAPI specifications of the Twitter and GitHub API’s. Twitter’s definition file is very small, while GitHub’s is absolutely massive. This serves as a good comparison between this static-site-generator-approach vs. Swagger UI for a smaller amount of endpoints vs. a larger amount.

Results: Twitter API

Asset Sizes

The size of assets generated by Hugo was just 8 KB, or 0.008 MB. The size of the Swagger UI assets is 18 MB. This signifies a 2,250x reduction in reference documentation asset sizes for the relavively small Twitter API definition.

Performance

There was a 45% improvement (69 -> 100) in the Google Lighthouse Performance score when I generated reference docs for Twitter’s API with my Hugo shortcode rather than Swagger UI.

Lighthouse Performance Score of 100

The Lighthouse Performance Score is 100 when using my example code to generate Twitter API Reference docs with Hugo

Lighthouse Performance Score of 69

The Lighthouse Performance Score is 69 when using Swagger UI to generate Twitter API Reference docs

Results: GitHub API

Asset Sizes

The size of assets generated by Hugo was 2.6 MB. The size of the Swagger UI assets is always the same 18 MB. This signifies a 7x reduction in reference documentation asset sizes for the huge GitHub API definition.

Performance

There was a 67% improvement (36 -> 60) in the Google Lighthouse Performance score when I generated reference docs for GitHub’s API with my Hugo shortcode rather than Swagger UI.

Lighthouse Performance Score of 60

The Lighthouse Performance Score is 60 when using my example code to generate GitHub API Reference docs with Hugo

Lighthouse Performance Score of 36

The Lighthouse Performance Score is 36 when using Swagger UI to generate GitHub API Reference docs

Conclusion

As you can see from the results above, huge improvements in documentation site performance can be realized by processing OpenAPI specification files with the static site generator build process. The major downside when compared with Swagger is that some coding is required to customize the display of these reference documents, and a new build is required for each update of the specification file.

As I mentioned before, I think Swagger UI is a great tool. However, I do not think it is a tool that works well with static site generators. If you are using a static site generator to write your API documentation, I encourage you to consider the approach I’ve described here rather than embed Swagger UI into your site. You’ll be able to preserve your site’s design aesthetic, as well as unlock huge performance benefits to visitors of your site. This just might enhance the overall experience for users of your API.