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/messagesFor an SSE channel, read the stream over HTTP:
curl -N http://localhost:3000/messagesHow messages are generated#
For each message the mock server, in order of preference:
- Uses a defined
examplesvalue, when present. - Otherwise generates a value from the message
payloadschema, 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
receiveoperation 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.