API · Openapi

OpenAPI vs AsyncAPI — A Comparison

OpenAPI describes request/response HTTP endpoints; AsyncAPI describes event-driven messaging over Kafka, MQTT, and WebSockets. Why most real systems need both, and where the tooling still lags.

John Kihiu12 min read

OpenAPI and AsyncAPI get compared like they're competitors, but they describe different halves of the same system. OpenAPI (formerly Swagger) documents synchronous request/response HTTP APIs — you call an endpoint, you get a response, the interaction is over. AsyncAPI documents event-driven, message-based APIs — a service publishes an event to a Kafka topic or an MQTT broker, and any number of consumers react to it whenever they get around to reading it. Most systems past a certain size have both a REST API and an event bus, so the real question isn't "which one" — it's how to keep both specs honest as the system grows.

What each spec actually describes

An OpenAPI document describes paths, HTTP methods, request/response schemas, status codes, and auth requirements — everything a client needs to call your REST API without reading the source. AsyncAPI describes channels (topics, queues, routing keys), the message schemas that flow through them, and the protocol binding (Kafka, AMQP, MQTT, WebSockets) that determines how a client actually connects. The spec formats look similar on purpose — AsyncAPI was explicitly modeled on OpenAPI's structure — but the underlying interaction models are fundamentally different: OpenAPI assumes a client waits for a response, AsyncAPI assumes producers and consumers are decoupled in time and don't share a request/response cycle at all.

YAML · OPENAPI PATH DEFINITION
paths:
  /orders/{id}:
    get:
      operationId: getOrder
      parameters:
        - name: id
          in: path
          required: true
          schema: { type: string }
      responses:
        '200':
          description: Order found
          content:
            application/json:
              schema: { $ref: '#/components/schemas/Order' }
        '404':
          description: Order not found

The AsyncAPI side of the same system

The AsyncAPI equivalent for the same domain doesn't describe a request — it describes what gets published when an order changes state, and who's expected to be listening. There's no response code because there's no synchronous caller waiting; a consumer that's down for ten minutes just processes the backlog when it comes back, assuming the broker retains messages long enough.

YAML · ASYNCAPI CHANNEL DEFINITION
channels:
  order.created:
    publish:
      operationId: onOrderCreated
      message:
        payload:
          type: object
          properties:
            orderId: { type: string }
            customerId: { type: string }
            total: { type: number }
            createdAt: { type: string, format: date-time }
  order.shipped:
    subscribe:
      operationId: notifyOrderShipped
      message:
        payload:
          $ref: '#/components/schemas/ShipmentEvent'
A schema mismatch fails differently in each world

A breaking change to an OpenAPI response shows up immediately — the client's next request either 400s or the deserializer throws. A breaking change to an AsyncAPI message can sit silent for hours: the consumer keeps reading events, silently drops the field it expected, and the failure surfaces downstream as missing data rather than an error. Schema validation at the point of publish catches this before it ships; catching it at the point of consumption is much more expensive.

Why you usually need both

A typical system has a REST API for things a client needs an immediate answer to — fetch this order, submit this payment, check this status — and an event bus for things other services need to react to without the original caller waiting around: inventory decrementing when an order ships, a notification service sending an email, an analytics pipeline recording the event. Trying to force one model to do the other's job is where systems get awkward — polling a REST endpoint every few seconds to simulate an event feed wastes resources and adds latency; trying to get a synchronous answer out of a fire-and-forget event bus means bolting a reply-channel convention onto something that wasn't built for it. Documenting both halves properly, in the format built for each, is what lets a new engineer understand the system without reading every service's source.

Tooling maturity is not close

OpenAPI has a decade of tooling behind it: code generators for nearly every language, mock servers, contract-testing frameworks, API gateways that read the spec directly for validation and routing, and every major API management platform speaks it natively. AsyncAPI tooling has caught up a lot in the last few years — code generation, an official Studio for editing specs visually, generators that scaffold consumer/producer boilerplate for Kafka and MQTT — but it's still noticeably behind: fewer IDE integrations, fewer gateways that understand it out of the box, and far fewer engineers who've internalized the format compared to OpenAPI. Teams adopting AsyncAPI should expect to write more glue themselves and lean on the spec as living documentation even where tooling doesn't yet automate as much as it does on the REST side.

Wrapping up

OpenAPI and AsyncAPI aren't rivals — they document two different interaction models that coexist in most real systems: synchronous request/response and asynchronous event-driven messaging. The practical move is to spec both halves honestly, validate message schemas at publish time rather than trusting consumers to handle drift gracefully, and accept that AsyncAPI tooling, while improving fast, still asks more manual work of you than the mature OpenAPI ecosystem does.

John Kihiu
Acumatica ERP Developer · Laravel Engineer

Independent software engineer in Nairobi specialising in Acumatica customisations, Laravel backends, and tax fiscalisation integrations across East and Southern Africa.