Orientation
Content Processing
Search Infrastructure
Rendering and UI
Developer Tooling
How It Works
The following files were used as context for generating this wiki page:
"AsyncAPI Generation" is a robust subsystem dedicated to transforming complex AsyncAPI specifications into structured, navigable documentation. By leveraging a modular architecture, it abstracts the complexity of specification parsing and data normalization, enabling developers to integrate event-driven API documentation into their workflows with minimal friction. The subsystem primarily solves the problem of "specification drift"—the gap between technical documentation and implementation—by providing both runtime generation tools and static export capabilities.
Architecturally, the subsystem is built on a "builder" pattern that processes AsyncAPI specifications into a normalized, internal format, which then flows into dedicated layout engines. These engines generate the virtual files required by the broader Fumadocs documentation pipeline. By separating concerns between document loading, schema dereferencing, and output building, it ensures that changes in the specification format are isolated, while UI components can rely on a consistent, unified model for rendering.
The system interacts extensively with lower-level document processing utilities, such as dereferencing tools that resolve $ref links and bundling utilities that normalize external schema references. By integrating directly into the fumadocs-core ecosystem, it functions as a loader plugin or a build-time generator, facilitating a seamless transition from raw AsyncAPI JSON/YAML files to interactive, high-fidelity documentation pages.
The subsystem begins by transforming raw input (URLs, local file paths, or objects) into a standardized LoadedDocument. This process is governed by loadDocument in packages/asyncapi/src/utils/document/load.ts. The central utility, bundle, is used to normalize the schema, ensuring that all components are accounted for, and a check is performed to confirm the document contains the required asyncapi version field.
The loading process is designed with caching as a first-class citizen. Within packages/asyncapi/src/server/index.tsx, the createAsyncAPI factory function initializes a schemaMap (a Map<string, Promise<LoadedDocument>>). When getSchema is called, the system first checks this cache unless disableCache is explicitly enabled. This design prevents redundant network requests or filesystem reads, which is critical in dynamic server environments where documentation might be rendered on every request.
Note
When using getSchema for documents not explicitly listed in the input array of AsyncAPIOptions, the system issues a console warning and skips caching for the resulting LoadedDocument to prevent unintentional memory usage.
Sources: packages/asyncapi/src/server/index.tsx:75-101, packages/asyncapi/src/utils/document/load.ts:7-22
The generation flow follows a structured pipeline: Schema → fromSchema (Builder) → toStaticData → VirtualFile. The fromSchema function in packages/asyncapi/src/utils/pages/builder.ts acts as the primary orchestration layer. It iterates over the AsyncAPI object, using a configuration-driven approach to map specification elements to OutputEntry structures. These entries define the hierarchical nature of the documentation, distinguishing between "pages" and "groups".
The data flow for generating a page entry is as follows:
fromSchema initializes the PagesBuilder context.create() callback within the builder is invoked for each detected operation or group.onEntries in the server index recursively processes these entries.getPageProps and toStaticData convert the raw schema into properties compatible with the UI layer (TOC, metadata, etc.).// Simplified representation of the entry building process
const list = fromSchema(id, schema.bundled, builderOptions);
function onEntry(entry: PageOutput | OperationOutput) {
const props = getPageProps(entry);
// Pushes to the virtual files collection
files.push({
type: 'page',
path: `${baseDir}/${entry.path}`,
data: { ... } // Compiled props and metadata
});
}Sources: packages/asyncapi/src/server/index.tsx:110-156, packages/asyncapi/src/utils/pages/builder.ts:57-87
The dereferencing subsystem is an essential step in resolving the complex, interlinked structure of AsyncAPI files. The function dereferenceBundledDocument (in packages/asyncapi/src/utils/document/dereference.ts) performs a synchronous dereference of the bundled document, creating a normalized schema object that the UI components consume.
Crucially, it tracks original references via a dereferenceMap. By providing the setOriginalRef callback to dereferenceSync from @fumadocs/api-docs/schema/dereference, it creates a bidirectional mapping between the flat, dereferenced object and the path where it originally lived. This allows the UI to display the exact original location of any schema node using the getRawRef method.
Sources: packages/asyncapi/src/utils/document/dereference.ts:6-34
AsyncAPI defines protocol-specific bindings (e.g., Kafka, MQTT, Solace) via BindingObject structures. The subsystem handles these dynamically through a mapping in packages/asyncapi/src/ui/bindings/protocols/index.ts. Each protocol implements a ProtocolBindingDefinition, which dictates how the specific server, channel, operation, or message binding is rendered.
The getProtocolBinding function performs a lookup on the protocolBindings object. If an unsupported protocol is encountered, it falls back to unknownBinding. This provides a safeguard for the documentation renderer, ensuring that unknown protocols do not crash the UI but rather display a default representation.
export function getProtocolBinding(protocol: string): ProtocolBindingDefinition {
const v = protocolBindings[protocol as never];
if (v) return v;
return {
...unknownBinding,
label: protocol,
};
}Sources: packages/asyncapi/src/ui/bindings/protocols/index.ts:25-55
Beyond the server-side runtime, the subsystem supports generating documentation at build-time through generateFiles in packages/asyncapi/src/generate-file.ts. This utility scans the input, converts all pages to text (Markdown/MDX format), and writes them to the specified output directory.
The process supports a watch mode using chokidar, which is particularly useful for local documentation development. When files change, the scan function recursively traverses the output entries, invoking toText for each node to generate its serializable form.
Caution
The beforeWrite lifecycle hook allows for external modification of the OutputFile[] array. Since this happens globally before write-to-disk, any destructive changes here will persist in the generated static output.
Sources: packages/asyncapi/src/generate-file.ts:56-81
The following table summarizes key interface structures used across the subsystem to maintain consistency between the raw specification and the rendered documentation.
Sources: packages/asyncapi/src/types/asyncapi-3.ts:10-192, packages/asyncapi/src/server/index.tsx:246-250