WebSocket channels
Add an AsyncAPI document beside your OpenAPI spec and its WebSocket channels join the API reference, next to the endpoints they belong with.
Markline reads AsyncAPI 2.x and 3.0 documents and shows each WebSocket channel in the API reference, as a section on a resource page, the way an operation is shown. There's nothing to generate and no second reference to maintain: the channels sit in the same sidebar, endpoint lists and page search as the REST operations.
Add a document
Put the document at api/asyncapi.json, beside api/openapi.json:
content/
api/
openapi.json
asyncapi.json
markline.json
That's all a site needs. To keep it somewhere else, set api.asyncapi.file,
relative to the content folder:
{
"api": {
"asyncapi": { "file": "api/realtime.json" }
}
}
The document is read as JSON. A file that isn't JSON, or isn't valid JSON, adds no channels and the build says why (see Build output).
What a channel shows
Each channel gets a section with:
- The connection URL, under a
WSSbadge: the first server's URL joined to the channel's address, with{parameters}set apart. - Its description, rendered as markdown.
- Path parameters, query parameters and headers, from the channel's
parametersand itswsbinding'squeryandheaders, in the same tables as an operation's. - The fields of each message, headed "Send · name" or "Receive · name", with the message's description.
- A handshake card: the opening
GETwith its required query parameters and its headers, and the101 Switching Protocolsreply, ready to copy. - A message list: one row per message, with an arrow for its direction, its name, a Send or Receive label and a one-line preview. A row opens to the full example, which you can copy or expand.
What the client sends is listed before what it receives. In the sidebar and the
resource's endpoint list a channel carries a WSS badge in place of a method.
Deprecated channels, messages and fields are labelled so (see Hiding and deprecation).
Where channels go
A channel goes on a resource page, so its URL sits beside the operations it belongs with. Markline picks the page in this order:
- Its tags. The first tag that names a resource, after
api.tagsrenames and merges. When every tag the channel has is hidden, the channel is hidden too. - Its path. The resource whose operations share the longest run of leading
path segments with the channel's address. Hosts and a leading version segment
(
/v1) don't count,{parameters}match each other whatever their names, and case doesn't matter. A tie goes to the resource listed first. - A WebSockets page. A channel that shares nothing with any resource goes on
a "WebSockets" page after the others, at
/api-reference/websockets. It can be renamed, moved, merged or put in a section like any other page (see below).
So /v1/text-to-speech/{voice_id}/stream-input lands on the resource that has
/v1/text-to-speech/{voice_id}, without any tags. Placement never changes the
URL of an existing page: channels only add sections to pages, and the
WebSockets page when it's needed.
Titles and message names
A channel's title is the first of:
- its
x-title(in 3.0, itstitle); - its operation's summary;
- the last plain segment of its address, made readable:
/v1/stream-inputreads "Stream input".
A summary that's only publish, subscribe, send or receive, in any case,
says nothing about the channel, so it isn't used as a title.
A message whose payload is a oneOf of kinds (one "client message" covering
start, text and close, say) is listed once per kind. Each kind is named by its
title, then the name its $ref points at, then its place ("Message 3"), and
keeps its own examples. A payload that's an ordinary object, or a oneOf beside
properties, stays one message.
Whose side the document is written from
AsyncAPI 2.x describes the server: an operation under publish is what the
server receives, so a client sends it, and subscribe is what a client
receives. Markline shows both from the client's side, as Send and
Receive, since that's who reads an API reference.
Some 2.x documents are written from the client's side instead, with publish
meaning what the server sends. If Send and Receive come out the wrong way round,
set api.asyncapi.perspective to "client":
{
"api": {
"asyncapi": { "perspective": "client" }
}
}
The build says once that it's reading the document from the client's side, so a
wrong setting is easy to spot. AsyncAPI 3.0 documents say what each side does
with action, so the setting doesn't change them. A 3.0 message that no
operation names has no direction; it's listed as "Message".
Hiding and deprecation
The extensions that hide parts of the OpenAPI spec hide parts of the AsyncAPI
document too: x-internal and x-hidden by default, or the list in
api.hideExtensions. Set one to true on:
- a channel, and it's left out;
- an operation (2.x
publishorsubscribe, a 3.0 operation), and its messages are left out. A channel whose operations are all hidden is left out with them. In 3.0, a message another visible operation also uses stays; - a message, or one kind in a payload
oneOf, and that message is left out; - a field in a payload, the query or the headers, and it's left out of the tables and of every example. A path parameter stays, since the URL needs it.
A flag beside a $ref hides that use only; the same schema stays wherever it's
used without one. Hidden channels, operations and messages are removed before
anything is built, so they reach no page, search entry or sitemap: a hidden
channel that would have been the only one on the WebSockets page takes that page
with it. Hidden fields are left out of every page.
A channel, message or field marked deprecated: true stays, labelled
"Deprecated": in the sidebar, in the resource's endpoint list, on the message's
row, and beside the field. With api.showDeprecated set to false, deprecated
channels and messages are left out, as deprecated operations are.
The WebSockets page and sections
The WebSockets page is a resource like the others, named WebSockets, so the
settings that arrange the reference arrange it too:
api.tags:labelrenames it (its URL stays/api-reference/websockets),mergeIntoputs its channels on another page, andhiddenhides it with its channels.api.tagOrder: listWebSocketsto move it up. Otherwise a WebSockets page that only channels make comes after every other page. If the spec has aWebSocketstag of its own, ormergeIntonames a page the spec already has, the channels join that page where it already is.api.sections: listWebSocketsin a section like any tag.
{
"api": {
"tags": { "WebSockets": { "label": "Realtime" } },
"sections": [
{ "title": "Speech", "tags": ["text-to-speech", "WebSockets"] }
]
}
}
Without api.sections, the sidebar's sections come from the OpenAPI spec's
x-tagGroups, then from the AsyncAPI document's own x-tagGroups, in the same
[{ "name", "tags" }] form. A group with exactly the same name as one of the
spec's joins it (names are matched as written, so Core and core are two
groups), and a page stays in the first group that lists it. Sections come before
pages in no section, so an AsyncAPI group that lists WebSockets moves it up to
its group's place, even ahead of the spec's pages when the spec has no groups of
its own. api.sections, even an empty list, replaces both.
Versions
In a versioned site, each version reads
its own document, from its own folder: v1/api/asyncapi.json beside
v1/api/openapi.json. api.asyncapi.file is relative to each version's folder.
A version without a document has no channels.
A version can have an AsyncAPI document and no OpenAPI spec, for an API that's only WebSockets, or a site that's only that. Its reference is its channels: with no resources to join, they're on its WebSockets page (named, merged or sectioned as above), which its reference opens on. The version picker offers it like any version with a spec, and the build lists its pages and prints its warnings the same way.
content/
api/openapi.json # v2, the default: REST and WebSockets
api/asyncapi.json
v3/api/asyncapi.json # v3: WebSockets only
Build output
A document Markline can't fully read never fails the build. What it can't read is left out, and the build prints one line about it, once (with the version, for a version other than the first):
- a file that isn't JSON, or isn't valid JSON;
- an AsyncAPI version other than 2.x or 3.0, or none;
- a
$refthat points outside the document, at nothing, or back at itself; - the client-side reading, when
perspectiveis"client".
Not yet
- YAML documents. Convert to JSON for now.
- Channels in the static search index and in
llms.txt. They are in the reference's own sidebar and page search. - Trying a channel from the page, the way Try it calls an operation.