AsyncAPI – Documenting Message-Driven APIs
Introduction
Event-driven architectures are everywhere. While OpenAPI has become the de-facto standard for synchronous request/response APIs, AsyncAPI covers asynchronous, message-driven communication: topics, queues, broker bindings, and message payloads can all be described in a machine-readable way. In this post I’ll show how OpenAPI and AsyncAPI complement each other, walk through a practical example (REST + Kafka event), include a simple diagram, an end-to-end workflow, broker-specific bindings, CI/contract-testing, and briefly touch on documentation options.
OpenAPI vs. AsyncAPI – who covers what?
- OpenAPI = HTTP/REST endpoints, requests/responses, status codes, authentication.
- AsyncAPI = channels (topics/queues), messages (payload + headers), protocol bindings (Kafka, AMQP, MQTT …), broker/server configuration.
Rule of thumb: If communication is synchronous and the client waits for a response → OpenAPI. If it’s asynchronous or event-driven → AsyncAPI. Most systems need both: REST for commands/queries, AsyncAPI for events/streams.
Practical example – combining both specs
Imagine a users
service with a REST API to sign up users and a Kafka topic emitting an event when a signup occurs.
OpenAPI describes the HTTP endpoint, AsyncAPI documents the event. Both specs can share schemas.
OpenAPI – signup endpoint
openapi: 3.0.3
info:
title: User API
version: 1.0.0
paths:
/signup:
post:
summary: Register user
requestBody:
required: true
content:
application/json:
schema:
$ref: './common-schemas.yaml#/components/schemas/UserCreate'
responses:
'201':
description: Created
content:
application/json:
schema:
$ref: './common-schemas.yaml#/components/schemas/User'
AsyncAPI – user.signed_up event on Kafka
asyncapi: '3.0.0'
info:
title: User Events
version: '1.0.0'
servers:
kafka:
url: kafka:9092
protocol: kafka
channels:
user.signed_up:
description: Topic for user signup events
publish:
message:
name: UserSignedUp
payload:
$ref: './common-schemas.yaml#/components/schemas/User'
Shared schemas
components:
schemas:
UserCreate:
type: object
required: [username, email]
properties:
username:
type: string
email:
type: string
format: email
User:
allOf:
- $ref: '#/components/schemas/UserCreate'
- type: object
properties:
id:
type: string
createdAt:
type: string
format: date-time
Signup flow
End-to-end demo workflow
- Keep
openapi.yaml
,asyncapi.yaml
, andcommon-schemas.yaml
in your repo. - Validate locally:
- OpenAPI:
openapi-cli lint openapi.yaml
(Redocly CLI). - AsyncAPI:
npx @asyncapi/cli validate asyncapi.yaml
.
- Mock with Microcks: import the AsyncAPI file and instantly mock Kafka/MQTT/AMQP messages.
- Generate documentation:
npx @asyncapi/cli generate fromTemplate asyncapi.yaml @asyncapi/html-template -o docs/asyncapi
. - Run integration tests: for Kafka, Testcontainers can spin up a broker and verify publish/consume.
Broker examples – bindings in action
- Apache Kafka: AsyncAPI Kafka bindings describe topic, partitioning, message key, acks. Commonly paired with schema registries.
- RabbitMQ (AMQP): bindings cover exchanges, queues, routing keys, TTLs.
- MQTT: bindings describe QoS levels and topic filters (useful for IoT).
Bindings let you add broker-specific metadata while keeping payload schemas broker-agnostic.
Contract testing & CI
Contract testing for message-driven systems is catching up with the HTTP world:
- Specmatic can use AsyncAPI definitions directly for contract tests against Kafka or Pub/Sub.
- Message Pact offers consumer-driven contracts for events.
- Microcks can validate that producers/consumers conform to AsyncAPI contracts.
In CI, add a job that validates and generates docs from asyncapi.yaml
. For GitHub Actions, the official asyncapi/cli
action supports validate
and generate
out of the box. Combine this with contract-test steps (Specmatic/Pact) for
confidence before deploying.
Documentation & discoverability
AsyncAPI specs can generate rich HTML docs via the Generator, or you can embed them directly into portals. Since many
teams use Docusaurus for API docs, AsyncAPI has both a React component (@asyncapi/react-component
) and community
plugins for Docusaurus integration. That makes it easy to keep event APIs discoverable alongside REST docs. I’ll cover
this in more detail in a dedicated follow-up post.
Conclusion
OpenAPI and AsyncAPI are not competitors but complementary tools. OpenAPI remains the right choice for synchronous request/response interactions, while AsyncAPI documents asynchronous event-driven communication. By sharing schemas between both specifications, you avoid duplication and ensure consistent contracts. AsyncAPI’s bindings let you capture broker-specific details, and its CLI and Generator fit neatly into CI pipelines. With contract testing (Specmatic, Pact) and mocking (Microcks), message APIs can be validated as rigorously as REST endpoints. Finally, integration into developer portals such as Docusaurus ensures discoverability and governance. If you are already describing your REST APIs with OpenAPI, adding AsyncAPI for your events is the logical next step.