~/ tanmen

~/posts / openapi-yaml

Managing OpenAPI files as a directory tree

· updated 2020-08-24

OpenAPI is great, isn’t it?

Given an OpenAPI file you can generate a client, stand up a mock server, use it for server-to-server communication, and see exactly what data is accepted. (Assuming it was written properly, that is.)

Personally, though, I have one complaint.

It is that an OpenAPI file is hard to manage in pieces.

When you cannot split it up, running it across several teams means conflicts happen often, and you end up inventing process to work around them.

Thankfully, other people feel the same way, and there are various solutions out there.

The official syntax also lets you split things up to some degree, via $ref.

But the official $ref does not solve the problem at the root either.

The problem with the official $ref

As the documentation shows, $ref can be written in a great many ways.

But the places you may use it are fixed.

The parts where conflicts happen are basically under paths and under components/schemas.

For example, making the following additions on separate branches produces a conflict:

paths:
  ...
  /A: # team A adds a new endpoint
    $ref: "./paths/A.yml#"
  /B: # team B adds a new endpoint
    $ref: "./paths/B.yml#"

At that scale the conflict is easy to resolve, so “just live with it” is a legitimate option.

I consider the conflict happening at all to be the problem, though, so I ruled that option out.

As far as I could find, no library resolves conflicts of this kind, so I wrote one that lets you manage yaml without conflicts.

openapi-yaml

Design

To split yaml without conflicts, adding or removing an endpoint must not change the properties directly under paths, and adding or removing a model must not change what sits under components/schemas.

This design solves that with the directory structure.

Here is what it looks like when you use openapi-yaml:

@startmindmap
skinparam monochrome true
skinparam ArrowColor White
* <&folder>Root
** <&file>openapi.yml
** <&folder>paths
*** <&folder>@pets
**** <&file>get.yml
*** <&file>@pets@{id}.yml
** <&folder>components
*** <&folder>schemas
*** <&folder>securities
*** ...
@endmindmap

The @ in file names stands in for /
It exists because / cannot be used in a file name

Root/openapi.yml only needs to contain the following, and you will probably never touch that file again. The other files will hardly ever conflict either.

...
paths:
    $dir: "./paths"
components:
    $dir: "./components"

Source code

openapi-yaml lives in this repository:

https://github.com/tanmen/openapi-yaml

It is MIT licensed, so feel free to modify and use it however you like.

Contributions and issues are welcome, so please open an issue if something does not work for you.

Wrapping up

There are still plenty of rough edges when working with OpenAPI.

Parts of the specification itself are vague, and there are awkward corners nobody has touched precisely because nobody is bothered by them.

Still, it is a specification that is widely used and carries real weight, so I intend to keep contributing and building libraries that make it nicer, and to keep the OpenAPI world lively.

The honest caveat

This post was mainly about document-driven development.

OpenAPI is usually distributed by the server, though, and with server-driven development none of the problems above apply.

The reason they disappear is that in server-driven development you do not manage the OpenAPI document at all: you receive an automatically generated OpenAPI file from the running API.

Since no document actually exists, the conflicts that come from managing one cannot happen.

If document-driven development is not a requirement for you and OpenAPI conflicts are causing pain, shifting to server-driven development is a perfectly good move.