Meet Redocly CLI: The Modern OpenAPI Sidekick
Redocly CLI is a brilliant new tool from the folks who made ReDoc, the first beautiful API reference documentation tool powered by OpenAPI. This CLI tool goes a lot further than documentation, and helps with “linting” (automated API Style Guides), and solves the biggest problem that I had previously been lumbered with trying to maintain: bundling!
History
Back in 2016 I built Speccy, with the help of the excellent Mike Ralphson who built OAS Kit which handled some of the internal logic. Speccy was the first CLI linter that would take programmable rulesets, arguably allowing you to create automated API Style Guides, a powerful component of any API Governance program. It also wrapped ReDoc, which are the time didn’t have a CLI. Then to make supporting multi-file documents easier it would bundle them up, and you could export that document with a bundle command.
Speccy was abandoned by WeWork shortly after I left, and the void that created was partially filled by Spectral, partly swagger-parser, and various other piecemeal bits and pieces made by various teams with various support for OpenAPI v3.0 or v3.1.
Redoc CLI is here to restore the one-tool to help you do all the most common things, without needing to login to some web interface or muck around with JavaScript code just to preview some OpenAPI.
Let’s talk about linting, previewing, bundling, and something new: splitting.
Linting
When I joined Stoplight it was brilliant to see they’d secretly been working on a souped up rewrite of Speccy which could do infinitely more than Speccy’s linter could. They dropped the CLI docs and bundling as other tools were handling that, leaving Spectral hyper-focused on linting.
As powerful as Spectral became, it can be pretty complicated to built rulesets. I battled through making rulesets covering APIs You Won’t Hate, URL Versioning, OWASP Security, but it was complicated even when I had access to the core developers. I favoured the NPM-module approach because it allowed me to shim together a test suite that became a rudimentary development environment.
Part of the complexity comes from the idea that Spectral could be a rules engine for any structured JSON/YAML data, meaning not just OpenAPI, but AsyncAPI, Kubernetes manifests, GitHub Actions, or anything else. Flexibility comes at a cost, and the cost here was the inability to use named identifiers pointing to specific bits of the OpenAPI document like Speccy did. Spectral uses a barely known standard called JSONPath which is like a combination of CSS and regular expressions.
# The headers of any 429 response
$..responses[429].headers
# Each security scheme
$.components.securitySchemes[*]
# A list of paths
$.paths[*]~
# A list of names for all the header parameters
$..parameters[?(@.in === 'header')].name
# Mime Types for error responses
$.paths[*]..responses[?(@property.match(/^(4|5)/))].content.*~
That’s not ideal, but it gets bit worse… even if you are familiar with JSONPath, this used JSONPath-Plus. Weeeeell no it used a modified version of JSONPath-Plus that sometimes was not compatible… It all reminded me far too much of the State of Markdown (tl;dr its an absolute state).
ReDocly CLI aims to simplify this with named identifiers for all the OpenAPI objects like Operation
and Response
, and as I was writing this they released AsyncAPI support objects too. Combining these named identifiers with filters that can help you get more specific is a simple yet powerful combination. These three filters are exceptionally useful:
filterInParentKeys: [get, put]
filterOutParentKeys: [delete]
matchParentKeys: /^p/
With Spectral you need to arcane spells like this to target responses with a status code 2XX or 4XX:
given: "$.paths[*]..responses[?(@property.match(/^(2|4)/))]",
Using the Redocly linting logic you’d swap that out for something like this:
- subject:
type: Response
matchParentKeys:
- /^2/
- /^4/
The named Response
object removes the need for rule builders to worry about the structure of the rest of the document ($.paths[*]..resposnes
), and the match parent keys allows you to ignore bits you’re not fussed about. There’s still regex in there, but you don’t have to smush it all into a one-liner.
I am excited with this new approach, and whilst I’ve not had a chance to try and test it to this limits, I can see a huge amount of potential here. Give it a go and see how far you get.
Previewing
My favourite way to spin up API documentation is pointing a CLI tool at an openapi.yaml
and having it launch a server that’ll change as I work on it. If it can watch and hot load then even better.
This is exactly how Redocly CLI’s preview-docs
command works.
redocly preview-docs openapi.yaml
Then when everything is looking ship-shape, I want to be able to run a single command to have it build static HTML which I can dump on Amazon S3, GitHub Pages, Netlify, or anywhere else static HTML is happy.
This is exactly how Redocly CLI’s build-docs
command works.
redocly build-docs openapi.yaml --output=docs/index.html
Incredibly simple, and handles gargantuan API description documents like GitHub Enterprise Server API with ease.
Bundling & Splitting
Having one massive OpenAPI document with thousands (or tens of thousands!) of lines is annoying, hard to work with, and prone to conflicts if two people dare do anything completely unrelated to each other. To avoid this developers split their API descriptions into multiple documents, then link it all back together with $ref
.
Unfortunately there is incomplete tooling out there which does not support $ref properly or at all, and you need a workaround. That workaround is some form of bundling (also known as resolving or dereferencing).
$ redocly bundle api/openapi.yaml -o tmp/output-bundled.yaml
bundling api/openapi.yaml...
📦 Created a bundle for api/openapi.yaml at tmp/output-bundled.yaml 105ms.
That one command seems to handle huge documents quickly, and the quality of the bundled YAML is lovely. It will put everything into named components
, unlike some other bundling tools which… do less beautiful things. Bundling differences need a whole other post, but… suffice to say its good enough a human could look at it without scratching their head, which also makes it look a lot better in any OpenAPI documentation tool you throw it at.
The opposite of bundling is splitting, and whilst you should not need to bundle then split in the same workflow, you may at different times find yourself needing both.
Perhaps somebody has just written their first OpenAPI description and the document is massive, or perhaps they’ve generated the OpenAPI from HTTP and it’s huge.
Split will take that massive file, slice it up into a sensible folder structure, and link the pieces together with $ref
just like I have done entirely manually a bunch of times before.
I thought I’d try and trip Redocly CLI up by using the GitHub Enterprise Server OpenAPI description but it happily sliced it up.
$ redocly split ghes-3.0.json --outDir split/ghes/
code .
🪓 Document: ghes-3.0.json is successfully split
and all related files are saved to the directory: split/ghes/
ghes-3.0.json: split processed in 31970ms
Genuinely impressive stuff there, I’ll be using that in the future instead of manually mushing YAML around and hoping I got it right.
Summary
Redocly CLI is a brilliant modern OpenAPI tool that does a lot, and does it well. There’s good intentions behind modular tools that do one thing and one thing well, but much of this logic is required and multiple tools end up being awkwardly chained.
Redocly CLI is therefore a great replacement for Swagger CLI (which I have now deprecated), as it handles linting (validation but better) and does bundling in a far more useful way, plus the beautiful documentation with a live reload, and much more.
So, head on over to Redocly CLI and give it a whirl!