How to Write Your First AsyncAPI Specification

The AsyncAPI Specification is a comprehensive specification language for describing asynchronous messaging APIs. Like a rocket bound for the International Space Station, its popularity has shot up over the last few years — and continues to do so — what with the rising adoption of IoT protocols and event-driven API architectures. So, what if you want to get in on some of the action, documenting your messaging API with AsyncAPI?

In this beginner’s guide to the AsyncAPI Specification, we’ll walk you through the process of creating your specification file, step-by-step. We’ll focus on the five objects every spec needs — the asyncapi version, info, servers, channels, and components — providing some mildly space-themed code snippets along the way.

Tooling: AsyncAPI Playground

First things first: tooling. Like the OpenAPI Specification on which it was based, you can create AsyncAPI Specification files in either JSON or YAML. This means you can write the spec for your API using any text editor of your choice.

With that said, there is a dedicated editor for AsyncAPI specs called AsyncAPI Playground. Not only does it display your specification file in a chic and legible way, but it also validates and renders the spec as you work on it. For your first AsyncAPI spec, we’d definitely recommend you use it.

Writing Your AsyncAPI Spec: Start to Finish

So, you’ve opened up the Playground, and you’re ready to start describing your API. As promised, we’ll show you how to write the spec object-by-object, from start to finish!

All objects marked with an asterisk (*) are required.

AsyncAPI version*

A must-have for any AsyncAPI spec is the AsyncAPI version, which is denoted by the asyncapi key. In this case, we’ll go for the most recent version of the specification:

asyncapi: 2.0.0

Info*

Next up is the info object, which houses API metadata. Specifically, it consists of six fields — title, version, description, termsOfService, contact, and license — of which only title and version (both strings) are required.

If you choose to fill out the other fields, bear in mind that description and termsOfService are also strings, whereas contact and license use the respective Contact Object and License Object formats defined by the specification.

Let’s opt for the middle-ground and include a few optional fields in our info object:

info:
  title: Rocket API
  version: '1.0.0'
  contact:
    name: Space Agency
    url: https://spaceagency.example.com
  license:
    name: Apache 2.0
    url: https://www.apache.org/licenses/LICENSE-2.0

Servers

Also important is the servers object, which defines where and how the client can communicate with various message brokers or other servers. This object is a map of server objects (notice that’s now a singular server), which themselves must contain a url and protocol, and may include other fields like security information in the form of a Security Requirement Object.

servers:
  production:
    url: api.spaceagency.example.com
    protocol: mqtt

Channels*

With channels, which are akin to the paths used in OpenAPI, we can start getting into the real functionality of the API. Each channel object should be named after the appropriate URI, and may contain the following six fields: $ref, description, subscribe, publish, parameters, bindings.

The subscribe and publish objects describe if and how — you guessed it — the client can subscribe or publish to that channel, in the form of an Operation Object. Also of note are parameters and bindings, which describe (respectively) any parameters included in the channel name and any protocol-specific channel properties.

Here’s what the channels object might look like:

channels:
  rocket/{rocketId}/status:
    parameters:
      rocketId:
        $ref: '#/components/parameters/rocketId'
    subscribe:
      summary: Receive status updates for a rocket.
      message:
        $ref: '#/components/messages/rocketStatus'

Components

In that last code sample, you might have noticed two references to components objects. In a way, these are the bread and butter of the AsyncAPI Specification, providing detailed, reusable objects for a long list of object types:

  • schemas
  • messages
  • securitySchemes
  • parameters
  • correlationIds
  • operationTraits
  • messageTraits
  • serverBindings
  • channelBindings
  • operationBindings
  • messageBindings

It’s all these components that allow the spec to precisely describe an API’s behavior. To keep things simple, let’s look at the two component references we used inside the channels object:

components:
  messages:
    rocketStatus:
      payload:
        type: object
        properties:
          ...
  parameters:
    rocketId:
      description: The ID of the rocket.
      schema:
        type: string

Putting the Pieces Together

If we put all the pieces together, we end up with a very rudimentary AsyncAPI spec that defines a single channel to which the client can subscribe, the parameters they need to provide to do so, and what payloads they can expect to get back:

asyncapi: 2.0.0
info:
  title: Rocket API
  version: '1.0.0'
  contact:
    name: Space Agency
    url: https://spaceagency.example.com
  license:
    name: Apache 2.0
    url: https://www.apache.org/licenses/LICENSE-2.0
servers:
  production:
    url: api.spaceagency.example.com
    protocol: mqtt
channels:
  rocket/{rocketId}/status:
    parameters:
      rocketId:
        $ref: '#/components/parameters/rocketId'
    subscribe:
      summary: Receive status updates for a rocket.
      message:
        $ref: '#/components/messages/rocketStatus'
        
components:
  messages:
    rocketStatus:
      payload:
        type: object
        properties:
          ...
  parameters:
    rocketId:
      description: The ID of the rocket.
      schema:
        type: string

And here’s what that looks like when rendered in the Playground:

Obviously, the AsyncAPI Specification allows you to describe your API in much more detail. To do so, you’ll want to refer to the documentation, which explains the many AsyncAPI objects in all their glory, and the GitHub, where you can see more examples of the specification in action.

Final Thoughts

Whether you want to track the status of rockets or something more mundane, the AsyncAPI Specification is the standard of choice for defining asynchronous APIs. This short guide has looked at the five basic objects you ought to include in your first AsyncAPI spec, but there’s plenty more to it. Thankfully, the specification is familiar, intuitive, and well-documented.