/nestjs-asyncapi

NestJS AsyncAPI module - generate the documentation of your event-based services using swagger-like decorators

Primary LanguageTypeScriptMIT LicenseMIT

Donate to support Ukraine (only verified charities)

Please opt-in for monthly payment, if possible.

Other ways to help

  • Spread the word. Add banners to your projects
  • Demand severe sanctions against Russia and support for Ukraine from your leaders
  • Cancel any business with Russian companies (stop supporting Russian economy, and reduce your own risks)
  • Reach out to Ukrainian friends, offer help
  • Get rid of Russian software, dependencies and infrastructure
  • Educate yourself and others on the Russian threat, read reputable news. Check out common misbelief.
  • Protest against war. Don’t be silent

For foreign warriors

Foreign warriors who are willing to join the resistance can join the International Legion of Territorial Defense

REPOSITORY README:

Nest Logo

A progressive Node.js framework for building efficient and scalable server-side applications.

NPM Version Package License NPM Downloads CircleCI Coverage Discord Backers on Open Collective Sponsors on Open Collective

Description

AsyncApi module for Nest.

The idea is to generate AsyncApi documentation (for event-based services like websockets) similar to nestjs/swagger.

Documentation example

Current state: package can generate AsyncApi contract document and serve html (similar to swagger-ui). Early beta, any contribution is welcomed.

Installation

full installation

$ npm i --save nestjs-asyncapi

nestjs-async api package doesn't require chromium (which is required by asyncapi lib), so u can skip chromium installation by setting PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true environment variable.

$ PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true npm i --save nestjs-asyncapi

Quick Start

Document is composed via decorators.

Define AsyncApi service class by AsyncApiService decorator

  @AsyncApiService()

Define publish/subscribe methods by AsyncApiPub/AsyncApiSub decorators

  class AnySwaggerExampleDto {
    @ApiProperty()
    readonly name: string;
  }

  @AsyncApiPub({
    channel: 'test',
    summary: 'Send test packet',
    description: 'method is used for test purposes',
    message: {
      name: 'test data',
      payload: {
        type: AnySwaggerExampleDto,
      },
    },
  })

  @AsyncApiSub({
    channel: 'signal',
    summary: 'Subscribe to signal packet',
    description: 'method is used for test purposes',
    message: {
      name: 'test data signal',
      payload: {
        type: AnySwaggerExampleDto,
      },
    },
  })

Usage example:

gateway file:

import {
  ConnectedSocket,
  MessageBody,
  OnGatewayInit,
  OnGatewayDisconnect,
  SubscribeMessage,
  WebSocketGateway,
  WebSocketServer,
} from '@nestjs/websockets';
import { Namespace, Server } from 'socket.io';
import { Logger } from '@nestjs/common';
import { Socket } from 'socket.io-client';
import { AsyncApiPub, AsyncApiService, AsyncApiSub } from 'nestjs-asyncapi';

@AsyncApiService()
@WebSocketGateway({ transports: ['websocket'], namespace: 'cats-ws' })
export class CatsGateway implements OnGatewayInit, OnGatewayDisconnect {
  @WebSocketServer()
  server: Server;
  private logger: Logger = new Logger(CatsGateway.name);

  afterInit(nsp: Namespace) {
    this.logger.log(`WS server init: ${nsp?.name}`);
  }

  handleDisconnect(client: Socket) {
    this.logger.log(`IOClient disconnected: ${client.id}`);
  }

  @SubscribeMessage('test')
  @AsyncApiPub({
    channel: 'test',
    summary: 'Send test packet',
    description: 'method is used for test purposes',
    message: {
      name: 'test data',
      payload: {
        type: AnySwaggerExampleDto,
      },
    },
  })
  test(@ConnectedSocket() client: Socket, @MessageBody() data: string) {
    this.logger.log(`data from client ${client.id} : ${JSON.stringify(data)}`);
    this.server.emit('test', data);
  }

  @AsyncApiSub({
    channel: 'signal',
    summary: 'Subscribe to signal packet',
    description: 'method is used for test purposes',
    message: {
      name: 'test data signal',
      payload: {
        type: AnySwaggerExampleDto,
      },
    },
  })
  async emitSignal(boardUUID: string, data: Record<string, any>) {
    this.server.to('test').emit('signal', data);
  }
}

main file:

import 'source-map-support/register';

import { NestFactory } from '@nestjs/core';
import { NestExpressApplication } from '@nestjs/platform-express';
import { AppModule } from './src/app.module';
import { AsyncApiDocumentBuilder, AsyncApiModule, AsyncServerObject } from 'nestjs-asyncapi';

const port = 4001;
const host = '0.0.0.0';
const docRelPath = '/async-api';

async function bootstrap() {
  const app = await NestFactory.create<NestExpressApplication>(AppModule);

  const asyncApiServer: AsyncServerObject = {
    url: 'ws://localhost:4001',
    protocol: 'socket.io',
    protocolVersion: '4',
    description: 'Allows you to connect using the websocket protocol to our Socket.io server.',
    security: [{ 'user-password': [] }],
    variables: {
      port: {
        description: 'Secure connection (TLS) is available through port 443.',
        default: '443',
      },
    },
    bindings: {},
  };

  const asyncApiOptions = new AsyncApiDocumentBuilder()
    .setTitle('Cats SocketIO')
    .setDescription('Cats SocketIO description here')
    .setVersion('1.0')
    .setDefaultContentType('application/json')
    .addSecurity('user-password', { type: 'userPassword' })
    .addServer('cats-server', asyncApiServer)
    .build();

  const asyncapiDocument = await AsyncApiModule.createDocument(app, asyncApiOptions);
  await AsyncApiModule.setup(docRelPath, app, asyncapiDocument);

  return app.listen(port, host);
}

const baseUrl = `http://${host}:${port}`;
const startMessage = `Server started at ${baseUrl}; AsyncApi at ${baseUrl + docRelPath};`;

bootstrap().then(() => console.log(startMessage));