npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

asyncapi-jsdoc

v2.0.0

Published

Generates AsyncAPI specifications from JSDoc comments and serves interactive UI via Express.

Readme

asyncapi-jsdoc

A code-first approach to event-driven API documentation for Node.js, mirroring the popular swagger-jsdoc developer experience but tailored for AsyncAPI v3.0.0.

npm version license bundle size npm downloads

asyncapi-jsdoc parses JSDoc comments containing @asyncapi tags, extracts YAML content, validates the schema using the official @asyncapi/parser, resolves $ref references so components can be shared across channels/files, and serves the interactive web UI via an Express middleware.


🛠️ Features

  • Code-First documentation: Define event-driven architectures directly alongside event handlers or controllers.
  • AsyncAPI v3.0.0 ready: Decouples channels and operations natively.
  • Robust YAML parsing: Preserves space margins for indentation-sensitive YAML by cleaning JSDoc leading asterisks line-by-line.
  • Official Schema Validation: Integrated with @asyncapi/parser (version 3.x) to ensure compliance before rendering.
  • Automatic $ref Resolution: Share a schema or message across multiple channels/JSDoc blocks via $ref and components.schemas/components.messages — the spec returned by generateAsyncAPISpec always comes back fully dereferenced (inlined), so downstream consumers (JSON Schema validators, the UI, custom tooling) never see a raw pointer.
  • Precise Error Tracking: Pinpoints the exact file and line number of any malformed YAML JSDoc comment.
  • Interactive UI Middleware: Renders a gorgeous, dark-themed, high-contrast UI (powered by @asyncapi/react-component standalone bundle via CDN).

📦 Installation

npm install asyncapi-jsdoc

Ensure you have express installed if you intend to serve the interactive web UI:

npm install express

📝 JSDoc @asyncapi Comment Structure (v3.0.0)

Under AsyncAPI v3.0.0, channels and operations are decoupled. The channel defines the physical endpoint and messages, while the operation defines the action (send or receive).

[!IMPORTANT] Because channel names typically contain slashes (e.g. user/signup), referencing them under operations requires using the escaped JSON Pointer representation ~1 (e.g. #/channels/user~1signup).

Here is a visual template of how JSDoc comment blocks should be structured:

/**
 * @asyncapi
 * channels:
 *   user/signup:
 *     address: user.signup
 *     messages:
 *       signupMessage:
 *         $ref: '#/components/messages/SignupMessage'
 * operations:
 *   userSignup:
 *     action: receive
 *     channel:
 *       $ref: '#/channels/user~1signup'
 * components:
 *   messages:
 *     SignupMessage:
 *       summary: Event triggered when a new user registers in the system.
 *       payload:
 *         type: object
 *         properties:
 *           id:
 *             type: string
 *             format: uuid
 *           email:
 *             type: string
 *             format: email
 */
export function handleUserSignup(data: any) {
  // Your logic here...
}

[!NOTE] The $ref: '#/components/messages/SignupMessage' above is not just cosmetic — generateAsyncAPISpec resolves it. The value returned to your app has channels.user/signup.messages.signupMessage fully inlined with the actual SignupMessage payload, not the pointer. This is how you reuse a schema or message across multiple channels, or even multiple JSDoc blocks/files: define it once under components.schemas/components.messages (in any file scanned by apis), then $ref it from as many places as you like — the merge across files happens before resolution, so it doesn't matter which file defines the component and which file references it. Set dereference: false if you need the raw, unresolved shape instead.


🚀 Quick Start Example

Here is a complete setup using Express and TypeScript:

1. File: events.ts

/**
 * @asyncapi
 * channels:
 *   orders/created:
 *     address: orders.created
 *     messages:
 *       orderCreatedMessage:
 *         $ref: '#/components/messages/OrderCreatedMessage'
 * operations:
 *   orderCreated:
 *     action: send
 *     channel:
 *       $ref: '#/channels/orders~1created'
 * components:
 *   messages:
 *     OrderCreatedMessage:
 *       summary: Publishes an event when a new order is created.
 *       payload:
 *         type: object
 *         properties:
 *           orderId:
 *             type: string
 *           amount:
 *             type: number
 */
export function emitOrderCreated() {
  console.log('Order created event emitted!');
}

2. File: server.ts

import express from 'express';
import { generateAsyncAPISpec, serveAsyncApi } from 'asyncapi-jsdoc';

const app = express();
const port = 3000;

async function startServer() {
  try {
    // 1. Generate and validate the AsyncAPI spec from JSDoc
    const spec = await generateAsyncAPISpec({
      definition: {
        asyncapi: '3.0.0',
        info: {
          title: 'My Event API',
          version: '1.0.0',
          description: 'Microservices and event messaging documentation',
        },
        servers: {
          rabbitmq: {
            host: 'localhost:5672',
            protocol: 'amqp',
          },
        },
      },
      apis: ['./events.ts'],
      validate: true, // Performs schema validation under the hood
    });

    // 2. Mount the UI and JSON Router middleware
    app.use(serveAsyncApi(spec, { path: '/asyncapi' }));

    // 3. Optional health path
    app.get('/', (req, res) => {
      res.send('Server active. View docs at <a href="/asyncapi">/asyncapi</a>');
    });

    app.listen(port, () => {
      console.log(`🚀 Test server running successfully!`);
      console.log(`📄 Spec JSON: http://localhost:${port}/asyncapi.json`);
      console.log(`🎨 Interactive UI: http://localhost:${port}/asyncapi`);
    });
  } catch (error: any) {
    console.error('❌ Critical error starting server:', error.message);
    process.exit(1);
  }
}

startServer();

⚙️ API Configuration Specifications

generateAsyncAPISpec(options)

Scans your codebase for @asyncapi tags, aggregates channels/components, and validates schema parameters.

  • options (Object):
    • definition (Record<string, any>): Baseline AsyncAPI specification (must contain asyncapi and info).
    • apis (string[]): Array of glob patterns defining target file sources. Required — at least one pattern must match at least one file, or generateAsyncAPISpec throws. There is no "static spec only" mode; the JSDoc scan is always mandatory.
    • validate (boolean, optional): Triggers @asyncapi/parser schema validation when true. Defaults to true.
    • dereference (boolean, optional): Resolves/inlines every local $ref (#/...) in the merged spec before returning it, so components.schemas/components.messages reused via $ref are fully expanded wherever they're referenced. Defaults to true. Set to false to get back the raw merged spec with $ref pointers untouched (the pre-1.1.0 behavior). A $ref chain that loops back on itself (e.g. a schema referencing itself, directly or through another schema) throws a descriptive error identifying the cycle, since it cannot be represented as inlined JSON.

serveAsyncApi(spec, options)

Delivers Express routes containing the raw JSON spec and the interactive react documentation dashboard.

  • spec (Record<string, any>): Compiled JSON specification.
  • options (Object, optional):
    • path (string, optional): Base route path. Defaults to /asyncapi. Serves the UI at <path> and raw JSON at <path>.json.
    • cssUrl (string, optional): External CDN path override for @asyncapi/react-component CSS styling.
    • scriptUrl (string, optional): External CDN path override for @asyncapi/react-component JS standalone bundle.

🧪 Development & Testing

Run local tests (utilizes the native Node.js test runner against the compiled dist/):

npm run build
npm run test

Build the library:

npm run build

Lint and format:

npm run lint       # ESLint (airbnb-base + TypeScript)
npm run lint:fix    # auto-fix what ESLint can
npm run format      # Prettier --write

Commit conventions

This repo uses Husky git hooks:

  • pre-commit: runs npm run build && npm test, then lint-staged (ESLint + Prettier on staged files).
  • commit-msg: validates the message against Conventional Commits via commitlint (e.g. fix: ..., feat: ..., chore: ...).

Commits that don't follow this format, or that fail lint/tests, are rejected locally before they're created.

📄 License

MIT