AsyncAPI 3.0.0 Release Notes

Jonas Lagoni

·9 min read

The new version of the AsyncAPI specification - 3.0.0 - is now available and is packed with goodies! Some which clears up confusions, some adding features, others improving maintainability.

To make the information as digestible as possible, we have split it up related information into different digestible chunks.

  • If you want to get an overview of all the changes done in v3 - Then you are in the right place!
  • If you want to get an overview of all the breaking changes between v2 and v3, i.e. migration guide - Read the AsyncAPI 3.0.0 migration guide

Overview

This post will give you an overview of all the changes done in v3.

Operation, channel and message decoupling

In v2, it has never been possible to re-use channels, because it was directly coupled with operations of an application.

In v3, this is now possible, with the mindset, a channel and message should be detached from the operations performed. This means for any message broker, for example Kafka, channels now ONLY define topics and the messages it contains. For REST interfaces it's all the paths and corresponding messages across all request types. For WebSocket, it's all the messages flowing through the WebSocket server. For Socket.Io, it defines all the rooms and messages therein.

This change makes the channels reusable across multiple AsyncAPI documents.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
asyncapi: 3.0.0
...
channels:
  UserSignup:
    address: user/signedup
    messages: 
      UserMessage: 
        payload:
          type: object
          properties:
            displayName:
              type: string
              description: Name of the user
operations:
  ConsumeUserSignups:
    action: receive
    channel: 
      $ref: "#/channels/UserSignup"

Issues: #618, #663 | Pull request: #806, #827

Publish and subscribe confusion

In v2, the publish and subscribe operation keywords has always been a subject of great confusion. Does it mean my application publishes to the channel? Does it mean you publish to me? Who are you in this context?

In v3 we try to clear this up. The only thing you need to worry about is you and your applicaion's behavior. No more confusion about what and who does what. We achieve this through two new operation keywords, send and receive, i.e. your application either send's or receive something.

This description of course alters slightly based on protocol, for the generic message brokers you produce or consume messages, but in the abstract AsyncAPI perspective, you still send or receive messages.

1
2
3
4
5
6
7
asyncapi: 3.0.0
...
operations: 
  SendUserSignedUp:
    action: send
  ReceiveUserSignedUp:
    action: receive

Issues: #829 | Pull request: #847

Request/Reply

One of the longest requested features, is request and reply... and it's finally here!

One thorn in the eye of this feature, has always been the publish and subscribe confusion, which complicated any efforts to achieve a workable solution. However, with that out of the way, we now have a solution 🔥

This feature has been designed with the following use-cases in mind.

  • Broker based messaging with well defined response topic + "correlationId".
  • Broker based messaging with per process individual inbox aka. "replyTopic" + "correlationId".
  • Broker based messaging with a temporary reply topic for a individual response
  • WebSocket, that has no topics, where the channel is a tcp connection where flow messages. Only "correlationId"
1
2
3
4
5
6
7
8
9
...
action: send | receive
reply:
  address:
    location: '$message.header#/replyTo'
  channel:
    $ref: '#/channels/userSignupReply'
  messages:
    - $ref: '#/components/messages/userSignedUpReply'

Read more about the Operation Reply Object here.

Issues: #94, #558 | Pull request: #847

Optional channels

We have seen many use-cases where an AsyncAPI document, have been used as a form of menu or collection of definitions. To do this in v2 would require a bit of a hack to achieve this. But in v3 channels are now entirely optional. This means that it's now possible to have a valid AsyncAPI document as such:

1
2
3
asyncapi: 3.0.0
components:
  ...

Issues: #829 | Pull request: #847

Unified referencing behaviors

In v2, there was two instances, where we used implicit references; server security configuration, by name referencing security requirement object in components, for channels to reference global servers by name. In an effort to stay as consistent as possible, we wanted to unify how references was used, that means that in v3, we ONLY use explicit references.

The server security information is also now an array instead of an object.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
asyncapi: 3.0.0
servers: 
  SomeServer:
    security:
      - $ref: '#/components/securitySchemes/SomeSecurity'
channels:
  SomeChannel: 
    servers: 
      - $ref: '#/servers/SomeServer'
...  
components:
  securitySchemes:
    SomeSecurity:
      ...
      scopes: [...]

Issues: #829 | Pull request: #852

Common metadata fields

There have been some inconsistency between which type of metadata fields are available in the different objects. Now we have added a few extra fields

  • added title, summary, and externalDocs fields in Server Object
  • added title, and summary fields in Channel Object
  • added title field in Operation Object and Operation Trait Object
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
asyncapi: 3.0.0
servers:
  SomeServer:
    title: Some Server title
    summary: This some server is for something
    externalDocs:
      ...
channels:
  SomeChannel:
    title: Some channel title
    summary: Some channel summary
operations:
  SomeOperation:
    title: Some operation title
    traits:
      - title: Some operation traits title

Issues: #795 | Pull request: #796

Cleaning up the root object

There was two meta information lingering in the root of the AsyncAPI object, which did not make much sense since we have the info object for all the meta information.

Therefore the root tags and externalDocs have been moved to the info object.

1
2
3
4
5
6
7
8
9
asyncapi: 3.0.0
info:
  ...
  externalDocs:
    description: Find more info here
    url: https://www.asyncapi.org
  tags:
    - name: e-commerce
...

Pull request: #794

Splitting out server URL into host and pathname

There have been some confusion about what url of a server should contain, is it both protocol + host + path? What about the protocol field then? Therefore each field now has their own field for the host, path, and protocol.

1
2
3
4
5
6
asyncapi: 3.0.0
servers:
  localhost:
    host: localhost
    path: /api/v1,
    protocol: mqtt

Issues: #547, #274 | Pull request: #888

More reusable objects in components

This is a bit of a mixture between some of the features, that all added a little to this. It's now possible to add more stuff under components:

  • Replies
  • Reply addresses
  • Tags
  • External docs
  • Operations
  • Channels
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
asyncapi: 3.0.0
components:
  ...
  replies:
    ...
  replyAddresses:
    ...
  tags: 
    ...
  externalDocs:
    ...
  operations:
    ...
  channels:
    ...

Issues: #829 | Pull request: #847, #792, #806, #827 | Migration details:

New trait behavior

Traits in v2, always replaced any duplicate properties that was defined both in traits and the associated object. This meant for example if the message traits defined headers and the message object did as well, only the message trait headers would be applied, because it overwrote anything you wrote in the message object.

In v3, this have now been changed so that main objects has a higher priority that what ever you define in traits.

For example, take a look at this message:

1
2
3
4
5
6
7
8
9
messageId: userSignup
description: A longer description.
payload:
  $ref: '#/components/schemas/userSignupPayload'
traits:
  - name: UserSignup
    title: User signup
    summary: Action to sign a user up.
    description: Description from trait.

Take notice of how description is not overwritten by the traits:

1
2
3
4
5
6
7
messageId: userSignup
name: UserSignup
title: User signup
summary: Action to sign a user up.
description: A longer description. # it's still description from "main" object
payload:
  $ref: '#/components/schemas/userSignupPayload'

Issues: #505 | Pull request: #517, #532, #907 | Migration details:

Schema format and payload definition

With schemas, one thing that has always been impossible was reusing schemas with different schema formats. Thats because the schema format information is part of the message object. That means that if you reference a Schema object, it has no information about the schema format because it's not located together.

In v3, schemaFormat has been removed from the message object and message trait object, and a new schema Object called Multi Format Schema Object has been introduced which pairs a schema together with it's schema format. Which now enables much better reusability:

1
2
3
4
5
6
7
8
9
10
11
12
13
asyncapi: 3.0.0
components:
  schemas:
    avroSchema:
      schemaFormat: 'application/vnd.apache.avro+yaml;version=1.9.0'
      schema:           
        type: record
        name: User
        namespace: com.company
        doc: User information
        fields:
          - name: displayName
            type: string

Issues: #622 | Pull request: #797, #910

Simplified Parameters

In v2, it was possible to use the full power of JSON Schema to define parameters, however, it introduced a lot of complexity to parameters, so for v3 it was dialed way down to only allow a very small set of properties. Parameters can now only have the following properties: enum, default, description, examples, and location.

1
2
3
4
5
6
7
8
asyncapi: 2.6.0
channels:
  user/{userId}/signup:
    parameters:
      userId:
        description: Id of the user.
        schema:
          type: string
1
2
3
4
5
6
7
asyncapi: 3.0.0
channels: 
  userSignup:
    address: user/{userId}/signedup
    parameters:
      userId:
        description: Id of the user.

Issues: #583 | Pull request: #935 | Specification information: https://www.asyncapi.com/docs/reference/specification/v3.0.0-next-major-spec.12#parameterObject

Editorial changes

We have removed the note that stated we strived to be compatibility with OpenAPI where possible, because with the recent changes, this is no longer the case. That said we of course still strive to make the different specs as interoperable as possible i.e. with Avro, RAML, OpenAPI Schema, etc.

Tooling support

The following official AsyncAPI tools are already updated to support 3.0.0 version of the specification:

TODO

Last but not least is the AsyncAPI Studio. Check out the Studio with this example.

Acknowledgements

Spec 3.0 have been a massive undertaking, so I would like to say a huge thank you! to everyone who have been involved, maybe you commented on your views, added to discussions, joined the live meetings, championed changes, reviewed proposed changes, list goes on, this section is for you!

Thank you, xxxxxxxxxxxxxxxxx

Photo by Alexandru Tudorache on Unsplash