How to avoid breaking changes in your REST API with OpenAPI
One of the challenges that you might encounter when you have an application which provides REST API is avoiding doing breaking changes when you update your API.
Introducing a breaking change in your REST API can possibly break thousands of clients that are consuming it. In an environment where you do not have control over clients, meaning you can't update them whenever you need, breaking change becomes really expensive and you might end up losing users.
Stating that your API is backward compatible gives your API consumers some confidence. It is not even that hard to achieve backward compatibility and there are multiple solutions that might help you achieving such API. I will focus on OpenAPI and I will try to show you how you can incorporate OpenAPI with some tools to avoid introducing breaking change in your API.
If you do not know what OpenAPI is you can read more about it in my post Specification-first API design.
OpenAPI Specification for Pet Store
Let's imagine that we have a REST API for Pet Store with following OpenAPI specification
It contains single resource which returns list of pets. There are no required parameters.
Detecting breaking changes
Now we will add a required path parameter to the resource, our specification will be following
This change to the API is a breaking change. This means that after this change all clients that do not send the required parameter will stop working because server requires it. If you follow specification-first API design, the breaking change might be detected by your reviewers in the design phase. However this expects that your reviewers will never miss such breaking change. Can we somehow detect this breaking change automatically?
Microsoft has an open source CLI tool to detect breaking changes between two OpenAPI specifications called
npm install -g firstname.lastname@example.org
This takes two specifications, you will have to create a copy of the old one and then compare it with the new one. Following is an output of
openapi-diff tool given the above Pet Store specifications
$ oad compare petstore.old.yaml petstore.new.yaml
"message": "The versions have not changed.",
"message": "The required parameter 'name' was added in the new version.",
You can see that the tool printed 2 messages. The first one has
NoVersionChange code and the second one has
NoVersionChangecode is only warning, it does not mean that it is a breaking change, hence the
Infotype. It just informs you that you haven't changed the
versionproperty in your specification (both are equal to
Errortype. This shows you that your new specification contains a breaking change. It also gives you a JSON ref to the resource where the breaking change occurred.
If you have your specification connected to your continuous integration, you can create a build breaker which will help you to detect breaking changes, based on the output of
openapi-diff, early in design phase. This will help you to mitigate introducing breaking changes because you would not be able to merge your proposal when the build failed.
As I mentioned above,
openapi-diff requires 2 specification files. If you have your specification versioned using Git then you would not have access to both old and new specification files by default. However there is an easy way to get the content of the file from different branch. If you want to get the content of
master you can call following command
$ git show master:petstore.yaml
Your whole script could look like
$ git show master:petstore.yaml > petstore.old.yaml
$ oad compare petstore.old.yaml petstore.yaml
OpenAPI is a very helpful standard that makes API design a very enjoyable process. If you want to mitigate breaking changes, you can combine OpenAPI with
openapi-diff tool and integrate it to your continuous integration to detect the breaking change early in the design phase.