Documentation

How to Use

Get EventScribe up and running in your NestJS application in just a few steps.

1

Install the Package

Install EventScribe using your preferred package manager.

terminalbash
npm install nestjs-eventscribe
2

Import the Module in NestJS

Import and configure the EventScribe module in your application's root module. The transport field accepts a single transport config or an array for multi-transport setups (e.g., Kafka + WebSocket simultaneously).

app.module.tstypescript
import { Module } from '@nestjs/common';
import { ConfigService, ConfigModule } from '@nestjs/config';
import { EventScribe, EventScribeTransport } from 'nestjs-eventscribe';

const isEventScribeEnabled = process.env.ENABLE_EVENTSCRIBE === 'true';

@Module({
  imports: [
    ...(isEventScribeEnabled
      ? [
          EventScribe.forRootAsync({
            imports: [ConfigModule],
            useFactory: (configService: ConfigService) => ({
              title: 'Demo Backend',
              description:
                'A demo backend service showcasing event capturing and Swagger UI documentation.',
              captureConsoleLogs: true,
              uiPath: 'my-swagger',
              transport: [
                {
                  transport: EventScribeTransport.KAFKA,
                  brokers: [configService.get('KAFKA_BROKERS')],
                },
                {
                  transport: EventScribeTransport.WEBSOCKET,
                },
              ],
            }),
            inject: [ConfigService],
          }),
        ]
      : []),
  ],
})
export class AppModule {}

This configuration sets up EventScribe with async factory pattern, allowing you to inject services like ConfigService for dynamic configuration. The transport field here uses an array to register both Kafka and WebSocket transports. You can also pass a single object if you only need one transport. The conditional pattern lets you enable/disable EventScribe via environment variables.

3

Configuration Options

EventScribe provides various configuration options to customize the module to your needs:

  • titleDisplay title shown in the EventScribe UI header
  • descriptionOptional subtitle/description shown below the title in the UI
  • uiPathURL path at which the EventScribe UI is served (default: 'events-scribe-ui')
  • transportA single transport config object or an array of transport configs for multi-transport setups
  • transport.transportThe event transport type (e.g., EventScribeTransport.KAFKA, EventScribeTransport.WEBSOCKET)
  • transport.brokersArray of broker addresses (required for Kafka transport, not needed for WebSocket)
  • captureConsoleLogsWhen true, captures all logs (console.*, NestJS Logger, winston, pino, etc.) during publish requests and returns them in the response (default: false)
4

Document Events with @ApiEvent

Use the @ApiEvent() decorator to document your event handlers. This decorator can be applied to any method to tell EventScribe about your events and their expected payload schemas.

Kafka Consumer Example:

user.controller.tstypescript
import {
  ApiEvent,
  EventScribeTransport,
} from 'nestjs-eventscribe';
import { EventPattern, Payload } from '@nestjs/microservices';

@ApiEvent({
  transport: EventScribeTransport.KAFKA,
  pattern: 'my-topic',
  payload: { type: UserCreatedBodyParamDto },
})
@EventPattern('my-topic')
async handleUserCreated(@Payload() data: UserCreatedBodyParamDto) {
  console.log('Consumer received from my-topic:', data);
  return;
}

WebSocket Gateway Example:

chat.gateway.tstypescript
import {
  ApiEvent,
  EventScribeTransport,
} from 'nestjs-eventscribe';
import {
  WebSocketGateway,
  SubscribeMessage,
  MessageBody,
  ConnectedSocket,
} from '@nestjs/websockets';
import { Socket } from 'socket.io';

@WebSocketGateway()
export class ChatGateway {
  @ApiEvent({
    transport: EventScribeTransport.WEBSOCKET,
    pattern: 'chat-message',
    payload: { type: ChatMessageDto },
    description: 'Send a chat message',
  })
  @SubscribeMessage('chat-message')
  handleChatMessage(
    @MessageBody() data: ChatMessageDto,
    @ConnectedSocket() client: Socket,
  ) {
    return { event: 'chat-message', data: { echo: data.message } };
  }
}

@ApiEvent Options

  • transportThe transport type for this event handler (e.g., EventScribeTransport.KAFKA). Can be set to null if not applicable.
  • patternThe topic or pattern name that this handler subscribes to. Can be set to null if not applicable.
  • payloadOptional payload configuration object describing the expected event data structure.
  • payload.typeA class (DTO) constructor that defines the schema of the payload. Used to generate documentation and validate test payloads.
  • payload.isArrayOptional boolean indicating whether the payload is an array of the specified type. Defaults to false.
  • descriptionOptional human-readable description of the event handler. Shown in the EventScribe UI to help users understand what the event does.
5

Define Schemas with @EventProperty

Use the @EventProperty() decorator to define the schema of your DTO properties. This decorator provides metadata for the EventScribe UI to generate accurate forms and documentation for your event payloads. If @EventProperty() is not used, EventScribe will automatically infer the schema from your DTO using class-validator decorators, class-transformer settings, and primitive type information.

@EventProperty Options

  • typeFactory that returns the nested DTO class or a primitive constructor. Required for nested objects/arrays of objects so the scanner can recurse. For primitive arrays, pass the constructor (e.g., () => Number) so the scanner knows the element type.
  • isArrayWhen true, the property is treated as an array of the resolved type. If omitted, the scanner infers array status from design:type === Array.
  • enumPass the enum object to expose its values in the UI. The dropdown will show all available enum options for the user to select.
  • excludeWhen true, the property is excluded from the schema. Use this to acknowledge a field exists without exposing it in the UI. Also suppresses the "partial coverage" warning for this property.
  • requiredWhen true, marks the property as required in the UI. The form will display a required indicator and validation will enforce that a value is provided. (default: true)
  • descriptionHuman-readable description shown in the EventScribe UI. Helps users understand the purpose and expected format of the field.
  • exampleExample value shown as placeholder in the UI form. Helps users understand expected values and format.

This example demonstrates nested objects, arrays, enums, and excluded fields:

create-order.dto.tstypescript
import { EventProperty } from 'nestjs-eventscribe';

enum OrderStatus {
  PENDING = 'PENDING',
  PROCESSING = 'PROCESSING',
  SHIPPED = 'SHIPPED',
  DELIVERED = 'DELIVERED',
}

class AddressDto {
  @EventProperty({ description: 'Street address', example: '123 Main St' })
  street: string;

  @EventProperty({ description: 'City name', example: 'New York' })
  city: string;

  @EventProperty({ description: 'ZIP code', example: '10001' })
  zipCode: string;
}

class OrderItemDto {
  @EventProperty({ description: 'Product ID', example: 'prod_123' })
  productId: string;

  @EventProperty({
    type: () => Number,
    description: 'Quantity ordered',
    example: 2
  })
  quantity: number;

  @EventProperty({
    type: () => Number,
    description: 'Price per unit in cents',
    example: 1999
  })
  price: number;
}

export class CreateOrderDto {
  @EventProperty({ description: 'Unique order ID', example: 'order_abc123' })
  orderId: string;

  @EventProperty({ description: 'Customer email', example: 'john@example.com' })
  customerEmail: string;

  @EventProperty({
    type: () => AddressDto,
    description: 'Shipping address for the order'
  })
  shippingAddress: AddressDto;

  @EventProperty({
    type: () => OrderItemDto,
    isArray: true,
    description: 'List of items in the order'
  })
  items: OrderItemDto[];

  @EventProperty({
    enum: OrderStatus,
    description: 'Current order status',
    example: OrderStatus.PENDING
  })
  status: OrderStatus;

  @EventProperty({
    type: () => Number,
    isArray: true,
    description: 'Applied discount codes',
    example: [10, 20]
  })
  discountPercentages: number[];

  @EventProperty({ exclude: true })
  internalNotes?: string; // Excluded from UI
}
6

Bootstrap Configuration

To access the EventScribe docs page, your application must listen on a port (not just start microservices). Here's the required bootstrap configuration for Kafka transport. WebSocket transport requires no additional bootstrap setup — gateways work automatically with the existing HTTP server.

main.tstypescript
import { NestFactory } from '@nestjs/core';
import { MicroserviceOptions, Transport } from '@nestjs/microservices';
import { AppModule } from './app.module';

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

  // Only needed for Kafka transport — WebSocket works with the HTTP server automatically
  app.connectMicroservice<MicroserviceOptions>(
    {
      transport: Transport.KAFKA,
      options: {
        client: {
          brokers: ['localhost:9092'],
        },
        consumer: {
          groupId: 'my-consumer-group',
        },
        run: {
          autoCommit: true,
        },
      },
    },
    { inheritAppConfig: true },
  );

  app.enableCors();
  await app.startAllMicroservices();

  // IMPORTANT: You must also listen on a port to serve the Swagger UI
  await app.listen(3001);
}

bootstrap();

Important: The app.listen() call is required for the EventScribe UI to be accessible. Without it, only the microservices will start, but the HTTP endpoints (including the EventScribe docs) won't be available.

Note: In multi-instance environments (e.g., Kubernetes, load balancers), Kafka publish responses are automatically relayed across instances via an internal Kafka topic — no additional configuration is required.

7

Start Your App

Start your NestJS application as usual. Once the app is running, you can access the EventScribe UI in your browser.

terminalbash
npm run start:dev

Tip: Once your app is running, navigate to http://localhost:3001/event-scribe-docs (or your configured uiPath) to access the EventScribe UI and start interacting with your events.

Need More Help?

Have questions, need assistance, or want to share feedback? Feel free to reach out for support and I'll be happy to help you get the most out of EventScribe.