In R&D, we love to design and build awesome pieces of software with the latest cutting-edge technology. But we also like to see that all the effort we put into our work makes sense for others by being adopted. One of the key aspects of this adoption is to provide well-defined and structured documentation.
For most of the developers, maintaining documentation is not the best part of the job. I often smile when I see the transformation of a happy developer announcing at the daily standup, “I’m done with my story, I’ll switch to a new one today!”
I ask my innocent question, “Is the documentation up to date?” I’ll let you guess the type of emotion that the brain activates. Please don’t judge me, I am also a major contributor to our documentation, so I share his burden.
I am referring to documentation in its collective form because we have many types of documentation to maintain:
From human-readable documentation that can be internal to sharing details on our design decisions and easing maintenance or customer-facing documentation to explain how to use and configure the product. The latter type is the kind of documentation that most people are likely familiar with.
The services we are building nowadays are more and more integrated with their ecosystems (customers and partners). We need the ability to describe the interfaces that the underlying systems can use to interact with each other. We are talking about yet another kind of documentation: machine-readable documentation.
Developers have always been “smartly lazy,” famously optimizing and automating for effectiveness. So one of the first questions they have to ask themselves is, “Can’t we just automatically generate human-readable documentation or even code from our machine-readable documentation?”
I want to introduce you to a promising new type of machine-readable documentation for our event-driven architectures called AsyncAPI.
What is AsyncAPI?
The AsyncAPI spec is an open-source application programming interface specification that reduces the learning curve since it is a sister spec to the OpenAPI spec (OAS was formerly called Swagger).
If you are completely new to OpenAPI, I encourage you to have a look at it first. OpenAPI is a specification and a set of open-source tools that help you design, build, document and consume REST APIs.
But for other types of APIs such as the ones that are required to implement an event-driven architecture, we lacked such specification and tooling.
Why are the event-driven APIs so different that they would need their own spec? There is a paradigm shift inherent in event-driven architecture:
- Publishers and subscribers are completely decoupled;
- Data exchange is no longer synchronous but asynchronous and
- The data in transit represents either a message or an event (aka significant change in-state).
This mental model and approach differ greatly from how RESTful APIs are shaped.
Take a closer look
Now let’s take a closer look at the specification itself and how it can apply to AMPLIFY Streams.
The first thing you must define is an application which is a generic definition of your service. The application can be any type (microservice, IoT, legacy server process) and written in any language, it doesn’t matter. What matters most is that it must either produce or consume messages.
Those messages are then exchanged on top of a protocol. AsyncAPI supports many of them such as:
- MQTT and
- even Kafka
AMPLIFY Streams relies on Server-Sent Events so we will use the HTTP protocol. You can also define several environments by providing a list of servers. In this example, we’ll document our prod environment only:
Once you’ve defined your application, servers and protocol, you must declare your channel(s).
A channel is a central component that enables publishers and subscribers to exchange their messages. Channels are also known as “topics,” “routing keys,” “event types” or even “paths” depending on the event-driven backbone you are using.
AMPLIFY Streams can take a URL as a parameter to transform into a stream and enable clients to subscribe to change events detected by the platform. While defining your channel, you’ll also have to define the type of message that can transit. In our case, we have two different types:
- The snapshot is the initial event containing the snapshot of data returned by the target API.
- followed by a patch which is a list of JSON patch operations to apply to the initial snapshot.
The message’s schema can be then further described thanks to the JSON schema in the component section of your AsyncAPI definition just as you would in the OpenAPI spec:
Here is an example of our snapshot event:
In the example above, you can also differentiate the attributes that are part of the headers from the attribute that are part of the payload in your messages.
If you want a complete view of the AMPLIFY Streams AsyncAPI spec, it is available on Github.
The corresponding generated doc is also available here.