Introducing the AsyncAPI specification
Fran Méndez 🍺 May 23, 2017
Originally posted on blog.hitchhq.com
The AsyncAPI specification is protocol-agnostic, so you can use it for APIs that work over MQTT, AMQP, WebSockets, STOMP, etc. Basically, IoT APIs and asynchronous microservices.
The specification is heavily inspired on OpenAPI (aka Swagger) and it’s designed to maintain as much compatibility as possible with it.
Why another specification?
At Hitch we’re in love with API machine-readable documentation formats (OpenAPI, RAML, etc.). We always encourage our customers to use a machine-readable definition for their APIs because of its numerous benefits. From better documentation and code generation to scalable API support.
Internally, we use a message-driven microservices architecture and we couldn’t have all those tools that we have with the HTTP APIs. Things you get because you have your API defined with a machine-readable documentation format. And, on top of that, at the same time we noticed that some of our customers were creating IoT APIs over MQTT and they couldn’t get all the benefits from Hitch because the existing specifications don’t support message-driven APIs.
It was time to come up with something good enough for everyone instead of having every company fighting its own battle. We can learn a lot together if we join forces. Let’s start!
A basic example
This example describes a very basic email service. It sends an email every time a new user signs up. It subscribes for the
hitch.accounts.1.0.event.user.signup event and, when received, sends the email and publishes a new message notifying the system that the email has been sent.
asyncapi: '1.0.0' info: title: 'Sign up email example' version: '1.0.0' baseTopic: 'hitch' host: 'asyncapi.hitchhq.com' schemes: - 'amqp' - 'mqtt' topics: accounts.1.0.event.user.signup: subscribe: $ref: '#/components/messages/userSignedUp' email.1.0.event.email.sent: publish: $ref: '#/components/messages/emailSent' components: messages: emailSent: summary: 'Email sent to user.' description: 'A message notifying an email has been sent.' payload: type: 'object' properties: user: $ref: '#/components/schemas/user' content: $ref: '#/components/schemas/content' userSignedUp: summary: 'A user has signed up.' payload: type: 'object' properties: user: $ref: '#/components/schemas/user' signup: $ref: '#/components/schemas/signup' schemas: content: title: 'content' description: 'The email content' type: 'string' id: title: 'id' description: 'Resource identifier' type: 'string' username: title: 'username' description: 'User handle' type: 'string' datetime: title: 'datetime' description: 'Date and Time of the message' type: 'string' format: 'date-time' user: type: 'object' required: - 'id' - 'username' properties: id: description: 'User Id' $ref: '#/components/schemas/id' full_name: description: "User's full name" type: 'string' username: $ref: '#/components/schemas/username' signup: type: 'object' required: - 'method' - 'datetime' properties: method: description: 'Signup method' type: 'string' enum: - 'email' - 'facebook' - 'twitter' - 'github' - 'google' datetime: $ref: '#/components/schemas/datetime'
If you are familiar with the OpenAPI specification I’m sure you already found lots of similarities. But, what’s this
topics section in the code? And what's this
accounts.1.0.event.user.signup? Let's dive into it.
The AsyncAPI specification is based on the assumption of two core concepts:
The way consumers can communicate with your API is based on messages. A message is a piece of information two or more programs exchange. Most of the times to notify the other end(s) that, either an event has occurred or you want them to perform an action.
Technically speaking the events and actions will always be sent in the same way. These are just messages and their content can be anything. So when we speak about the difference between events and actions, this is just a semantic differentiation of the content of the message. We do not enforce you to make any difference between them (although we encourage you to do it).
Martin Fowler’s talk on event-driven architectures at GOTO
A message can contain headers and a payload, however both are optional. To remain as much protocol-agnostic as possible, the specification allows you to define any kind of header.
Message-driven protocols usually contain something that can be found as a topic (MQTT), routing key (AMQP), destination (STOMP), etc. To some extent, they can compare to URLs in HTTP APIs. So when you send a message to your API it will be routed depending on the topic you published it on. It allows you to create APIs that subscribe to certain topics and publish to other ones.
There’s no standard way of naming topics so we recommend you to have a look at our proposal here.
So far we only have a code generator for Node.js (github.com/asyncapi/node-codegen). We would like to have you involved in generating one for your programming language of choice.
Documentation generators and API support
Are you interested in generating documentation for your AsyncAPI and/or setting up the Hitch API Assistant? We can definitely help! Drop us a line at email@example.com and we’ll get back to you.
The AsyncAPI specification will let us improve the interoperability and tooling around this type of APIs. Either it’s for the Internet of Things or for internal asynchronous microservices, we believe this will make the difference in the future of async APIs.