//

AsyncAPI specification updates

23.2.2022 | 6 minutes of reading time

Almost six months have passed since my introductory post about AsyncAPI , which is already an eternity in our fast-moving IT world. 😉 Shortly after publishing said post, more precisely in September last year, version 2.2.0 of the specification was released. At the beginning of February this year there was another so-called minor release to AsyncAPI version 2.3.0. Both versions are backward compatible with regard to the written API description, so that an update of the existing version value for the key asyncapi shouldn’t be a problem and the description is still valid afterwards.
I would also like to use the previously used description again, which has been optimized a bit in the meantime, and use it to perform the migrations to version 2.3.0 of the specification.

asyncapi: '2.1.0'
info:
  title: Order Service
  version: 1.0.0
  description: The service is in charge of processing orders
  contact:
    name: Daniel Kocot
    email: daniel.kocot@codecentric.de
  license:
    name: Apache 2.0
    url: https://www.apache.org/licenses/LICENSE-2.0.html
servers:
  rabbitmqInStaging:
    url: rabbitmq-staging.codecentric.de:{port}
    description: RabbitMQ Broker in staging environment
    protocol: amqp
    protocolVersion: '0.9.1'
    variables:
      port:
        default: '5672'
        enum:
          - '5672'
          - '15672'
  rabbitmqInProd:
    url: rabbitmq.codecentric.de:{port}
    description: RabbitMQ Broker in production environment
    protocol: amqp
    protocolVersion: '0.9.1'
    variables:
      port:
        default: '5672'
        enum:
          - '5672'
          - '15672'
channels:
  orderProcessed:
    publish:
      operationId: orderProcessedPub
      description: Payload of processed order
      message:
        $ref: '#/components/messages/orderProcessed'
      bindings:
        $ref: '#/components/messageBindings/amqp/bindings'
    subscribe:
      operationId: orderProcessedSub
      description: Payload of processed order
      message:
        $ref: '#/components/messages/orderProcessed'
      bindings:
        $ref: '#/components/messageBindings/amqp/bindings'
    bindings:
      amqp:
        $ref: '#/components/channelBindings/amqp'
components:
  schemas:
    OrderPayload:
      type: object
      properties:
        id:
          type: integer
          format: int64
          description: ID of received order
        customerReference:
          type: string
          description: Reference for the customer according the order
  messages:
    orderProcessed:
      name: orderProcessed
      title: Order Processed
      summary: Inform about a new processed order in the system
      contentType: application/json
      payload:
        $ref: '#/components/schemas/OrderPayload'
  channelBindings:
    amqp:
        is: routingKey
        exchange:
          name: orderExchange
          type: direct
          durable: true
          vhost: /
        bindingVersion: 0.2.0
  messageBindings:
    amqp:
      bindings:
        amqp:
          timestamp: true
          ack: false
          bindingVersion: 0.2.0

Assigning channels to servers

Let’s start directly with the first feature, the assignment of channels to servers, which has been available since AsyncAPI 2.2.0. This enables us to assign the respective channel to one or more specific servers. This is done by setting the optional servers property of type string related to the Channel Item object. Please note that the names of the servers always need to be the same as the names of the servers defined in the server object. We now adapt the above example to the feature accordingly.

asyncapi: '2.2.0'
info:
  title: Order Service
  version: 1.0.0
  description: The service is in charge of processing orders
  contact:
    name: Daniel Kocot
    email: daniel.kocot@codecentric.de
  license:
    name: Apache 2.0
    url: https://www.apache.org/licenses/LICENSE-2.0.html
servers:
  rabbitmqInStaging:
    url: rabbitmq-staging.codecentric.de:{port}
    description: RabbitMQ Broker in staging environment
    protocol: amqp
    protocolVersion: '0.9.1'
    variables:
      port:
        default: '5672'
        enum:
          - '5672'
          - '15672'
  rabbitmqInProd:
    url: rabbitmq.codecentric.de:{port}
    description: RabbitMQ Broker in production environment
    protocol: amqp
    protocolVersion: '0.9.1'
    variables:
      port:
        default: '5672'
        enum:
          - '5672'
          - '15672'
channels:
  orderProcessed:
    servers:
      - rabbitmqInStaging
    publish:
      operationId: orderProcessedPub
      description: Payload of processed order
      message:
        $ref: '#/components/messages/orderProcessed'
      bindings:
        $ref: '#/components/messageBindings/amqp/bindings'
    subscribe:
      operationId: orderProcessedSub
      description: Payload of processed order
      message:
        $ref: '#/components/messages/orderProcessed'
      bindings:
        $ref: '#/components/messageBindings/amqp/bindings'
    bindings:
      amqp:
        $ref: '#/components/channelBindings/amqp'
components:
  schemas:
    OrderPayload:
      type: object
      properties:
        id:
          type: integer
          format: int64
          description: ID of received order
        customerReference:
          type: string
          description: Reference for the customer according the order
  messages:
    orderProcessed:
      name: orderProcessed
      title: Order Processed
      summary: Inform about a new processed order in the system
      contentType: application/json
      payload:
        $ref: '#/components/schemas/OrderPayload'
  channelBindings:
    amqp:
        is: routingKey
        exchange:
          name: orderExchange
          type: direct
          durable: true
          vhost: /
        bindingVersion: 0.2.0
  messageBindings:
    amqp:
      bindings:
        amqp:
          timestamp: true
          ack: false
          bindingVersion: 0.2.0

Servers and channels as reusable objects

With version 2.3.0 servers and channels are available as reusable objects. Thus, the two elements only have to be defined within the components, then they can be referenced within the description. This is now also applied to our example description.

asyncapi: '2.3.0'
info:
  title: Order Service
  version: 1.0.0
  description: The service is in charge of processing orders
  contact:
    name: Daniel Kocot
    email: daniel.kocot@codecentric.de
  license:
    name: Apache 2.0
    url: https://www.apache.org/licenses/LICENSE-2.0.html
servers:
  staging:
    $ref: '#/components/servers/rabbitmqInStaging'
  production: 
    $ref: '#/components/servers/rabbitmqInProd'
channels:
  orderProcessed:
    servers:
      - staging
    publish:
      operationId: orderProcessedPub
      description: Payload of processed order
      message:
        $ref: '#/components/messages/orderProcessed'
      bindings:
        $ref: '#/components/messageBindings/amqp/bindings'
    subscribe:
      operationId: orderProcessedSub
      description: Payload of processed order
      message:
        $ref: '#/components/messages/orderProcessed'
      bindings:
        $ref: '#/components/messageBindings/amqp/bindings'
    bindings:
      amqp:
        $ref: '#/components/channelBindings/amqp'
components:
  schemas:
    OrderPayload:
      type: object
      properties:
        id:
          type: integer
          format: int64
          description: ID of received order
        customerReference:
          type: string
          description: Reference for the customer according the order
  servers:
    rabbitmqInStaging:
      url: rabbitmq-staging.codecentric.de:{port}
      description: RabbitMQ Broker in staging environment
      protocol: amqp
      protocolVersion: '0.9.1'
      variables:
        port:
          default: '5672'
          enum:
            - '5672'
            - '15672'
    rabbitmqInProd:
      url: rabbitmq.codecentric.de:{port}
      description: RabbitMQ Broker in production environment
      protocol: amqp
      protocolVersion: '0.9.1'
      variables:
        port:
          default: '5672'
          enum:
            - '5672'
            - '15672'
  messages:
    orderProcessed:
      name: orderProcessed
      title: Order Processed
      summary: Inform about a new processed order in the system
      contentType: application/json
      payload:
        $ref: '#/components/schemas/OrderPayload'
  channelBindings:
    amqp:
        is: routingKey
        exchange:
          name: orderExchange
          type: direct
          durable: true
          vhost: /
        bindingVersion: 0.2.0
  messageBindings:
    amqp:
      bindings:
        amqp:
          timestamp: true
          ack: false
          bindingVersion: 0.2.0

Contributor time

When looking at the description, it quickly becomes apparent that there is further potential with regard to reusability. This refers specifically to the variables for the server object. Exactly this caused me to post a Request for Comments (RFC) in the Git repo for the specification. In the meantime three pull requests followed out of the RFC. One for the specification itself and the other for the JSON schema . I submitted the third and final pull request for the JS parser shortly after the post was published. Now the feature can become a part of the next release (2.4.0) of the AsyncAPI specification. Hopefully ;). If you also have a feature in mind for an open-source project, I can only encourage you to put it into action. Only in this way can we help keep open source an integral part of our daily work.

New protocol bindings

After this short trip into the world of open source contributions I would like to take a look at the innovations regarding protocol bindings. What is actually meant by protocol bindings? Protocol bindings or short bindings describe the specifics of the used protocol. They are the basis of the architecture component, which is an essential part of the description with AsyncAPI. This can be done on the levels of server, channel, operation and message. Up to version 2.1.0 the following protocols are supported:

  • HTTP
  • Websockets
  • Kafka
  • AMQP 0.91
  • AMQP 1.0
  • MQTT
  • MQTT 5
  • NATS
  • JMS
  • SNS
  • SQS
  • STOMP
  • Redis
  • Mercure
  • IBM MQ

However, the binding for IBM MQ cannot be applied at the level of ‘operation’. With the last two releases two more bindings for Anypoint MQ and Solace have been added. More detailed information about the bindings can be found at Repo within the AsyncAPI Org in GitHub.

Outlook and summary: Beyond AsyncAPI version 2.3.0

In AsyncAPI, we find a very convenient way to describe event- and message-driven architectures and also to incorporate this description into existing processes of our API lifecycle, supported by CI/CD pipelines. It will be exciting to see how this and other specifications and the topics around the description of APIs will evolve in the coming years. So look forward to more posts on these aspects.

share post

Likes

0

//

More articles in this subject area\n

Discover exciting further topics and let the codecentric world inspire you.

//

Gemeinsam bessere Projekte umsetzen

Wir helfen Deinem Unternehmen

Du stehst vor einer großen IT-Herausforderung? Wir sorgen für eine maßgeschneiderte Unterstützung. Informiere dich jetzt.

Hilf uns, noch besser zu werden.

Wir sind immer auf der Suche nach neuen Talenten. Auch für dich ist die passende Stelle dabei.