Obviously, API design is much more than writing OpenAPI description doc. First of all, should it even be OpenAPI-based? If yes, using
openapi-cli will make your life a bit easier.
Stargate network consists of points in space identifiable via special addresses. When you dial an address (like a phone number), a wormhole is established between your gate and a gate at the address. Gates are physical objects, so they can be destroyed or moved to another address.
- Is the address accessible? Can we dial it?
- last known position in human terms (some addresses aren’t fixed points in space)
- Symbols used to dial it
- in galaxy and out of galaxy addresses
- unique address if it assigned to the gate
- Available gates at this address
- Environment: is it located above the surface, underwater, on the surface, or inside the ship?
- State: is the gate functional, destroyed, or buried?
- Address id
Next, operations. Nothing fancy, the bare minimum:
- List all addresses
- Get address info
- Add new gate info
- Get gate info
- Update gate info
Note: Don’t forget, not all APIs should be OpenAPI-based. If I needed to design a real Stargate system, I would go with a combo of something like GraphQL for lookup and event-based APIs for anything else (perhaps described with AsyncAPI).
Let’s create a place to store our OpenAPI description doc: YAML-based, multi-file git repo. You can read more about why’s in the Redocly docs.
The example repo is on the GitHub. At each step of the tutorial you’ll see links to commits as
We’re gonna use
create-openapi-repo to generate the initial structure and then tweak it a bit. Run
npx create-openapi-repo inside a directory where you want to generate API structure. One caveat to keep in mind: this tool will attempt to initialize a git repository and make a commit. Commit e5b40a5.
Let’s explore the generated files:
docsdirectory contains HTML template for reference doc. I’m deleting it, because I don’t need any custom template at the moment.
.gitignoreset up to ignore our beloved
distdirectory used for generating the final OpenAPI description doc.
.redocly.yamlis a configuration file for
openapi-cli. Because I removed
docsdirectory, I also need to remove
- Don’t forget to update the
package.jsonset up with the most common actions.
- Take your time to read
README.md. It explains what actions are available and provides a sample contribution guidelines. In the real project, you probably don’t want to have all guidelines there in one file.
- And last, but not least,
openapidirectory. This is where your API definitions live. Almost all subdirectories have a relevant
README.mdexplaining why these particular files were created as well as showing you other alternatives. I don’t want to copy-paste them here, so, go ahead and explore ;)
Now, let’s rewind the time. Stargate Network API contract is defined, commit c2b8d11. For more information about keeping your structure DRY read this and this. If you’re interested in reading/watching about it from me, just ping!
Note: The definitions are not state of art, even for this shallow design. Some problems with it were added consciously to simplify the example, others for fixing in later articles.
And I’m not even talking about how many details are omitted lore-wise!
package.json file there is already an action for previewing the docs:
openapi preview-docs. To execute it, run in the terminal
npm run start.
This starts a server with the generated Redoc reference documentation (docs). It watches changes from the disk; usually I keep it running while writing definitions and use it like a UI of Stoplight Studio or Insomnia Designer.
As you may (or may not) know, you can customize how Redoc looks like. This is done with
referenceDocs section in
.redocly.yaml (docs). Beware, some options are available only for premium edition.
Options can be divided into two categories: those that affect a theme (colors, fonts) and those for disabling or enabling certain features. For example,
requiredPropsFirst: true will display required properties before others:
See at commit c873f15.
From time to time you should lint with
openapi lint (docs). Let’s execute
npm run test.
➜ npm run test > email@example.com test /home/aviskase/Projects/openapi-cli-examples > openapi lint validating /home/aviskase/Projects/openapi-cli-examples/openapi/openapi.yaml...  openapi/openapi.yaml:1:1 at #/ Servers must be present. 1 | openapi: 3.0.3 2 | info: … | < 30 more lines > 33 | $ref: components/securitySchemes/api_key.yaml 34 | Error was generated by the no-empty-servers rule. /home/aviskase/Projects/openapi-cli-examples/openapi/openapi.yaml: validated in 27ms ❌ Validation failed with 1 error. run with `--generate-ignore-file` to add all problems to ignore file.
Oops! We have an error! Wait a second,
no-empty-servers rule complains that we must have
servers defined. But OpenAPI specification doesn’t state this as a required field, so our description doc is valid. Where does this error come from?
.redocly.yaml was generated, it included
lint: extends: - recommended rules: no-unused-components: warning
no-empty-servers comes from extending recommended set of rules. If it’s too strict, you may want to change to
minimal. Or change it to
all to get even more errors.
You can read more about other built-in rules in the docs. In the later article I’ll show how to add your own custom rules.
Most of the tools require single-file OpenAPI doc. That’s where bundling comes in. The
package.json is preconfigured with the script to run
openapi bundle -o dist which will save a generated file to the
dist directory. Usually it’s enough, but in some cases you may want to run with
--derefenreced flag to produce a file with internally inlined definitions (docs).
➜ npm run build > firstname.lastname@example.org build /home/aviskase/Projects/openapi-cli-examples > openapi bundle -o dist bundling /home/aviskase/Projects/openapi-cli-examples/openapi/openapi.yaml... 📦 Created a bundle for /home/aviskase/Projects/openapi-cli-examples/openapi/openapi.yaml at dist.yaml 28ms.
Hmm, what just happened? Instead of
./dist/<filename>.yaml we’ve got
Well, this is kinda bug or feature situation. If you check the code, this looks like an intended behavior, but nevertheless confusing. In short, if you have only one API definition,
-o seems to be expecting file path. If you have multiple API definitions (more about that in the upcoming part two), this option can point to directory.
In our example we have one API definition, so to fix we should change the script either to
-o dist/openapi or to
-o dist/openapi.yaml. Both will work the same, as long as
--ext equals to
yaml by default. If you want to generate file in JSON format, you can use
-o dist/openapi --ext json or
-o dist/openapi.json. If you want both formats, you need to run commands separately, see at commit a4e1e23.
One of the features that
openapi-cli doesn’t have, but I need all the time is to generate static Redoc html file. Yes, you’d want to invest in better solutions and processes at some point, but at smaller scale it’s ok to send a simple HTML file when someone from non-dev team wants to see the latest or prototype API thingy.
First, we need to install
npm i redoc-cli. Then, add a new script to
package.json to generate an HTML file inside
"docs": "redoc-cli bundle dist/openapi.yaml -o dist/redoc.html"
npm run docs and opening the generated file you’ll notice that all our custom options are gone. You see,
redoc-cli doesn’t support
.redocly.yaml configuration file. To fix this, add
prepareOptions.js which reads
.redocly.yaml and saves it to
.redoc.json Next, modify
docs script to run preparation script and use
"docs": "node plugins/prepareOptions.js && redoc-cli bundle dist/openapi.yaml -o dist/redoc.html --options .redoc.json"
See at commit ad6dacc.
Personally, I prefer different names for scripts:
npm run lint && npm run bundle && npm run docs
Then, all I do is jump between
npm run preview and
npm run build.
See at commit 51c078e.
That’s it for today. In the part two, I’ll show how to manage multiple definitions per repository.