# Understanding AsyncAPI's publish & subscribe semantics with an example

Author: [Mete Atamel](https://atamel.dev/) | Published: 2023-05-18 | Updated: 2024-05-08 | Topics: AsyncAPI, Event-driven, How To | Canonical: https://atamel.dev/posts/2023/05-18_asyncapi_publishsubscribe_refactor/


In [AsyncAPI](https://www.asyncapi.com/), a channel can have a `publish` and
`subscribe` operation. This can be confusing, depending on which perspective
you're considering (server vs. user) and what you're comparing against (eg.
WebSocket).

In this blog post, I want to go through an example to show you how to construct
your AsyncAPI file with the right `publish` and `subscribe` semantics. As a
bonus, I also show you how to refactor your AsyncAPI files with common
configuration.

## Publish vs. Subscribe semantics in AsyncAPI

Before we look into an example, here's a table that explains what `publish` and
`subscribe` mean in AsyncAPI and how it related to other similar concepts
such as WebSocket's `send` and `receive`.

| AsyncAPI Term | WebSocket Term | From Server Perspective | From User Perspective |
| --- | --- | --- | --- |
| Publish | Send | The channel receives the message | User publishes messages to the channel |
| Subscribe | Receive | The channel publishes the message | User subscribes for messages from the channel |

In my experience, it's easier to think of `publish` and `subscribe` from
user's perspective:

* A user **publishes** messages to a channel on a server.
* A user **subscribes** for messages from the channel on a server.

[Demystifying the Semantics of Publish and
Subscribe](https://www.asyncapi.com/blog/publish-subscribe-semantics) blog post
goes into more detail into this, if you want to read more.

## Account and Email Services

Now that we understand the basic semantics of `publish` and `subscribe`, let's look
into an example outlined in the excellent [Understanding AsyncAPIs with a Practical
Example](https://medium.com/event-driven-utopia/understanding-asyncapis-with-a-practical-example-ee2b4be221d8)
blog post to see if we can construct AsyncAPI definitions with the proper semantics.

Imagine you want to describe two microservices: `Account Service` emits an
`userSignedUp` event and `Email Service` receives that event:

![Account and Email services](https://camo.githubusercontent.com/36279498ca85bf6339a037e9d7ab665ef0b2b7990aec984433ff7463e2b18337/68747470733a2f2f6d69726f2e6d656469756d2e636f6d2f76322f726573697a653a6669743a3732302f666f726d61743a776562702f312a704e7649566959347835765a684f6c6e4d38647155772e706e67)

How do you define such an architecture in AsyncAPI?

## Account Service: AsyncAPI definition

Let's look at the `Account Service` first.

First, you need to define some metadata and the server:

```yaml
asyncapi: '2.6.0'
info:
  title: Account Service
  version: 1.0.0
  description: Processes user sign ups and publishes an event afterwards

servers:
  test:
    url: mqtt://test.mosquitto.org
    protocol: mqtt
    description: Test MQTT broker
```

Next, you need to define a channel for the server with `subscribe` or `publish`
operation. At this point, you need to ask yourself as a user: **Do I publish
messages to or subscribe messages from this channel?** It's the latter, because
the service emits `userSignedUp`, so the channel needs to have `subscribe` operation:

```yaml
channels:
  user/signedup:
    subscribe:
      operationId: publishUserSignedUp
      message:
        $ref: '#/components/messages/userSignedUp'
```

You can also see that the channel refers to a `userSignedUp` message. Let's
define that with a simple schema as well:

```yaml
components:
  messages:
    userSignedUp:
      name: userSignedUp
      title: User signed up message
      summary: Emitted when a user signs up
      contentType: application/json
      payload:
        $ref: '#/components/schemas/userSignedUpPayload'

  schemas:
    userSignedUpPayload:
      type: object
      properties:
        displayName:
          type: string
          description: Name of the user
        email:
          type: string
          format: email
          description: Email of the user
```

[account-service.yaml](https://github.com/meteatamel/asyncapi-basics/blob/main/samples/account-email-services/account-service-2.6.yaml)

## Email Service: AsyncAPI definition

In `Email Service`, you need to ask the same question: **Do I publish messages
to or subscribe messages from this channel?**. The answer is the former, because
the service receives messages, so the channel needs to have the `publish`
operation:

```yaml
channels:
  user/signedup:
    publish:
      operationId: receiveUserSignedUp
      message:
        $ref : '#/components/messages/userSignedUp'
```

The rest of the
[email-service.yaml](https://github.com/meteatamel/asyncapi-basics/blob/main/samples/account-email-services/email-service-2.6.yaml)
is identical to
[account-service.yaml](https://github.com/meteatamel/asyncapi-basics/blob/main/samples/account-email-services/account-service-2.6.yaml).

## Refactoring AsyncAPI definitions

As you've seen, 80% of
[email-service.yaml](https://github.com/meteatamel/asyncapi-basics/blob/main/samples/account-email-services/email-service-2.6.yaml) and
[account-service.yaml](https://github.com/meteatamel/asyncapi-basics/blob/main/samples/account-email-services/account-service-2.6.yaml)
are identical. Can we somehow refactor these? Yes we can!

We can put together the common parts of these two definitions (such as server,
message, schema) into a
[common.yaml](https://github.com/meteatamel/asyncapi-basics/blob/main/samples/account-email-services/common-2.6.yaml):

```yaml
# Common AsyncAPI config shared between Account and Email Services
servers:
  test:
    url: mqtt://test.mosquitto.org
    protocol: mqtt
    description: Test MQTT broker

components:
  messages:
    userSignedUp:
      name: userSignedUp
      title: User signed up message
      summary: Emitted when a user signs up
      contentType: application/json
      payload:
        $ref: '#/components/schemas/userSignedUpPayload'

  schemas:
    userSignedUpPayload:
      type: object
      properties:
        displayName:
          type: string
          description: Name of the user
        email:
          type: string
          format: email
          description: Email of the user
```

Then, in
[account-service-common.yaml](https://github.com/meteatamel/asyncapi-basics/blob/main/samples/account-email-services/account-service-common-2.6.yaml),
you can simply refer to the common server and message configuration:

```yaml
# Same as account-service.yaml except it uses common.yaml to share common config
# between email-service.yaml
asyncapi: '2.6.0'
info:
  title: Account Service
  version: 1.0.0
  description: Processes user sign ups and publishes an event afterwards

servers:
  $ref: 'https://raw.githubusercontent.com/meteatamel/asyncapi-basics/main/samples/account-email-services/common.yaml#/servers'

channels:
  user/signedup:
    subscribe:
      operationId: publishUserSignedUp
      message:
        $ref: 'https://raw.githubusercontent.com/meteatamel/asyncapi-basics/main/samples/account-email-services/common.yaml#/components/messages/userSignedUp'
```

Of course, you can do the same in
[email-service-common.yaml](https://github.com/meteatamel/asyncapi-basics/blob/main/samples/account-email-services/email-service-common-2.6.yaml).

---

In this blog post, I tried to explain the `publish` and `subscribe` semantics of
AsyncAPI with a simple example and also showed you how to refactor AsyncAPI
definitions with common configuration. There are two excellent blog posts
already published by the AsyncAPI community on this topic that I relied on, if
you want to read more:

* [Understanding AsyncAPIs with a Practical Example](https://medium.com/event-driven-utopia/understanding-asyncapis-with-a-practical-example-ee2b4be221d8)
* [Demystifying the Semantics of Publish and
  Subscribe](https://www.asyncapi.com/blog/publish-subscribe-semantics)

In the next blog post, I will show how how to combine the two of my favorite
open-source specifications: **CloudEvents** and **AsyncAPI**.

