Overview

AsyncAPI Mocking

Supported transports#

Transport When it is used Behavior
WebSocket (ws / wss) Channels whose servers use the ws/wss protocol (or declare a WebSocket binding). receive operations push a generated message when a client connects; send operations echo a generated reply to each inbound frame.
SSE (sse, or http/https with a receive operation) One-way, server-push channels over HTTP. A GET on the channel route opens an SSE stream and emits a generated message per receive operation.

Brokered protocols such as Kafka, MQTT, and AMQP require external broker infrastructure and are not served in-process. They can be added through the transports extension point.

Usage#

The mock server returns the Hono app and a websocket option. Pass both to @hono/node-server v2 to enable WebSocket channels:

import { serve } from '@hono/node-server'
import { createAsyncApiMockServer } from '@scalar/mock-server'
 
const document = {
  asyncapi: '3.1.0',
  info: { title: 'Chat', version: '1.0.0' },
  servers: {
    production: { host: 'localhost:3000', protocol: 'ws' },
  },
  channels: {
    messages: {
      address: 'messages',
      messages: {
        chatMessage: {
          contentType: 'application/json',
          payload: {
            type: 'object',
            properties: {
              user: { type: 'string' },
              text: { type: 'string' },
            },
            required: ['user', 'text'],
          },
        },
      },
    },
  },
  operations: {
    receiveMessage: { action: 'receive', channel: { $ref: '#/channels/messages' } },
    sendMessage: { action: 'send', channel: { $ref: '#/channels/messages' } },
  },
}
 
const { app, websocket } = await createAsyncApiMockServer({
  document,
  onMessage: ({ channel, direction, payload }) => console.log(direction, channel, payload),
})
 
serve({ fetch: app.fetch, port: 3000, websocket })

Connect a WebSocket client to the channel route to receive a generated message and echo replies:

npx wscat -c ws://localhost:3000/messages

For an SSE channel, read the stream over HTTP:

curl -N http://localhost:3000/messages

How messages are generated#

For each message the mock server, in order of preference:

  1. Uses a defined examples value, when present.
  2. Otherwise generates a value from the message payload schema, using the same generator as the REST mocker.

Channel addresses with path parameters (for example rooms/{roomId}) become route parameters (/rooms/:roomId).

The SSE transport emits one message per receive operation and then closes the stream, rather than streaming continuously. Reconnect (or let your SSE client reconnect) to get another batch.

Document detection#

The Docker mock server and CLI automatically detect AsyncAPI documents (by their top-level asyncapi field) and start the AsyncAPI mock instead of the REST mock — no extra flag is required.

Adding custom transports#

Transports are pluggable. A transport declares which channels it owns via supports() and registers them via register(). The first transport whose supports() returns true for a channel owns it; built-in WebSocket and SSE transports are checked first.

import { createAsyncApiMockServer, type MockTransport } from '@scalar/mock-server'
 
const signalrTransport: MockTransport = {
  name: 'signalr',
  supports: (channel) => channel.protocols.includes('signalr'),
  register: (channel, context) => {
    // `context` exposes the Hono `app` (for HTTP handshakes), Hono's `upgradeWebSocket`
    // helper (for socket upgrades), and `generateMessage(channel, messageId?)`.
    context.app.post(`${channel.route}/negotiate`, (c) => c.json({ /* ... */ }))
  },
}
 
const { app, websocket } = await createAsyncApiMockServer({
  document,
  transports: [signalrTransport], // appended after the built-in transports
})

Because register() receives both the Hono app and the upgradeWebSocket helper, protocols that negotiate over HTTP before upgrading to a socket (such as SignalR) fit without any core changes.

Updated

Was this page helpful?