Almost one year ago, I talked about AsyncAPI 2.6 and
how confusing its publish and subscribe operations can be in my Understanding
AsyncAPI’s publish & subscribe semantics with an
example post.
Since then, a new 3.0 version of AsyncAPI has been released with breaking changes
and a totally new send and receive operations.
In this blog post, I want to revisit the example from last year and show how to
rewrite it for AsyncAPI 3.0 with the new send and receive operations.
AsyncAPI 3.0
AsyncAPI 3.0 was released in December 2023. Since it’s a major version, it
has some breaking changes. These two pages does a good job explaining the
changes and the rationale behind them:
I won’t go through all the changes. For me, the biggest change is the separation
of operations and channels, and changing publish and subscribe operations to
send and receive.

Recap: Publish and subscribe operations in AsyncAPI 2.6
As a recap, AsyncAPI 2.6 has the following publish and subscribe
operations with these semantics:
| AsyncAPI Term | From Server Perspective | From User Perspective |
|---|---|---|
| Publish | Server receives the message | User publishes messages to the server |
| Subscribe | Server sends the message | User subscribes for messages from the server |
In 2.6, publish and subscribe operations are from user’s perspective.
New: Send and receive operations in AsyncAPI 3.0
In 3.0, the publish and subscribe operations are replaced with
send and receive operations. In Migrating to
v3 page, the rationale is
given as follows:
In v2, the publish and subscribe operations consistently caused confusion, even among those familiar with the intricacies.
When you specified publish, it implied that others could publish to this channel since your application subscribed to it. Conversely, subscribe meant that others could subscribe because your application was the one publishing.
In v3, these operations have been entirely replaced with an action property that clearly indicates what your application does. That eliminates ambiguities related to other parties or differing perspectives.
While I agree that publish and subscribe were confusing, I’m not sure if
send and receive are less confusing. You still need to talk about whose
perspective when defining these operations. AsyncAPI docs talk about
application but that’s not clear either. Does application refer to the code
sending the message (user) or the code receiving the message (server)?
In Async 3.0, send and receive operations are from server’s
perspective. An example will clarify.
Account and Email Services
Let’s revisit our example from last year. You have two microservices:
Account Service emits an userSignedUp event and Email Service receives that event:
How do you define such an architecture in AsyncAPI 2.6 vs 3.0?
Account Service
For Account Service, in 2.6, you had to define a channel with subscribe
operation because the user has to subscribe to receive messages:
asyncapi: 2.6.0
channels:
user/signedup:
subscribe:
operationId: publishUserSignedUp
message:
$ref: '#/components/messages/userSignedUp'
In 3.0, the channel does not have an operation anymore. Instead, there’s a new
publishUserSignedUp operation with send action that refers to the channel.
This is because the server sends the message:
asyncapi: 3.0.0
channels:
user/signedup:
address: user/signedup
messages:
publishUserSignedUp.message:
$ref: '#/components/messages/userSignedUp'
operations:
publishUserSignedUp:
action: send
channel:
$ref: '#/channels/user~1signedup'
messages:
- $ref: '#/channels/user~1signedup/messages/publishUserSignedUp.message'
Email Service
Similarly, in Email Service, in 2.6, you had to define a publish operation
because the user had to publish a message to the server:
asyncapi: 2.6.0
channels:
user/signedup:
publish:
operationId: receiveUserSignedUp
message:
$ref : '#/components/messages/userSignedUp'
However, in 3.0, the server receives a message, so the operation has
receive action:
asyncapi: 3.0.0
channels:
user/signedup:
address: user/signedup
messages:
receiveUserSignedUp.message:
$ref: '#/components/messages/userSignedUp'
operations:
receiveUserSignedUp:
action: receive
channel:
$ref: '#/channels/user~1signedup'
messages:
- $ref: '#/channels/user~1signedup/messages/receiveUserSignedUp.message'
In this blog post, I explored a small part of AsyncAPI and explained the
differences between 2.6 operations publish and subscribe and 3.0
operations send and receive. In a nutshell, in 2.6, you need to think from
user’s perspective and in 3.0 in server’s perspective when you define these
operations.
If you’re interested in learning more, I have a talk on CloudEvents and AsyncAPI and a repo with some samples: