Aviary
Channels

Broadcast

Push notifications to the browser in real time over socket.io. Each notifiable gets its own room; pair it with the database channel for a live in-app feed.

The broadcast channel pushes a notification to connected clients in real time over socket.io. It emits an event into a per-notifiable room, so a user sees the notification land without polling. It pairs naturally with the database channel — persist for the feed, broadcast for the live update.

Install

The channel uses Nest's websockets layer and socket.io, so install those peers alongside it:

pnpm add @dudousxd/nestjs-notifications-broadcast @nestjs/websockets @nestjs/platform-socket.io socket.io
npm install @dudousxd/nestjs-notifications-broadcast @nestjs/websockets @nestjs/platform-socket.io socket.io

Register the channel

app.module.ts
import { BroadcastChannelModule } from '@dudousxd/nestjs-notifications-broadcast';

@Module({
  imports: [
    BroadcastChannelModule.forRoot({ event: 'notification', namespace: '/ws' }),
  ],
})
export class AppModule {}

BroadcastChannelModule.forRoot() takes:

OptionTypeDefaultDescription
eventstring'notification'The socket.io event name emitted to clients.
namespacestringsocket.io namespace to mount the gateway on.
globalbooleantrueRegister globally so the channel is discoverable app-wide.

Registering the module also stands up a NotificationsGateway — the socket.io gateway that holds the server reference and emits into rooms. It's exported, so you can inject it to have clients join their room on connect (typically keyed by the same value you route on).

The notification side

Annotate the payload method with the @Broadcast() handle and return the real-time payload:

invoice-paid.notification.ts
import { type Notifiable, Notification } from '@dudousxd/nestjs-notifications-core';
import { Broadcast } from '@dudousxd/nestjs-notifications-broadcast';

@Notification()
export class InvoicePaid {
  constructor(private invoiceId: string) {}

  @Broadcast()
  toBroadcast({ notifiable }: ChannelContext): Record<string, unknown> {
    return { invoiceId: this.invoiceId, message: 'Your invoice was paid' };
  }
}

The Broadcast handle also works as a via() token (via() { return [Broadcast]; }) for explicit routing; implement BroadcastNotification alongside the decorator for compile-time checks.

toBroadcast() is optional when you route with an explicit via(). If it's absent the channel falls back to toArray(), and finally to a structural copy of the notification's own properties.

The target room

The room comes from the notifiable's routeNotificationFor('broadcast') — return the room name the client joined:

user.ts
export class User implements Notifiable {
  constructor(public id: number) {}

  routeNotificationFor(channel: string) {
    if (channel === 'broadcast') return `user.${this.id}`;
    return undefined;
  }
}

The channel emits the configured event with the payload to every client in that room.

Broadcast is fire-and-forget: there's no persistence, so a client offline at send time misses the event. Add the database channel to the same via() so the notification is also stored — the live push updates the UI now, the stored row backs the feed on next load.

On this page