Skip to content

Recipes

Cluster-wide WebSocket broadcast

Every instance subscribes to the same channel; publishing from anywhere reaches the sockets connected to all instances.

typescript
import { Injectable } from '@nestjs/common';
import { Subscribe, IPubSubMessage } from '@nestjs-redisx/pubsub';
import { NotificationGateway } from './types';

/**
 * Fan out server events to WebSocket clients on EVERY instance: each instance
 * subscribes to the same channel, so a message published anywhere reaches all
 * connected sockets cluster-wide.
 */
@Injectable()
export class RealtimeBridge {
  constructor(private readonly gateway: NotificationGateway) {}

  @Subscribe('broadcast.notifications')
  onNotification(message: IPubSubMessage<{ userId: string; text: string }>): void {
    this.gateway.broadcast(`notify:${message.data.userId}`, message.data);
  }
}

Cross-service domain events

Publish domain events from write paths and let any number of services react without coupling:

typescript
import { Injectable, Inject } from '@nestjs/common';
import { PUBSUB_SERVICE, IPubSubService } from '@nestjs-redisx/pubsub';
import { UserCreatedEvent } from './types';

@Injectable()
export class UserPublisher {
  constructor(
    @Inject(PUBSUB_SERVICE)
    private readonly pubsub: IPubSubService,
  ) {}

  async userCreated(user: UserCreatedEvent): Promise<void> {
    // Payload is JSON-serialized; returns the number of subscribers reached.
    const receivers = await this.pubsub.publish('user.created', user);
    if (receivers === 0) {
      // Pub/Sub is fire-and-forget: nobody was listening right now.
    }
  }
}

Consumers subscribe by exact channel or by family via patterns:

typescript
import { Injectable } from '@nestjs/common';
import { Subscribe, IPubSubMessage } from '@nestjs-redisx/pubsub';
import { UserCreatedEvent, NotificationGateway } from './types';

@Injectable()
export class UserEventsHandler {
  constructor(private readonly gateway: NotificationGateway) {}

  // Auto-subscribed on startup via discovery.
  @Subscribe('user.created')
  onUserCreated(message: IPubSubMessage<UserCreatedEvent>): void {
    this.gateway.broadcast('user:new', message.data);
  }

  // Redis glob patterns: *, ?, [..]
  @Subscribe({ pattern: 'order.*' })
  onAnyOrderEvent(message: IPubSubMessage): void {
    // message.pattern === 'order.*', message.channel === concrete channel
    this.gateway.broadcast(message.channel, message.data);
  }
}

Choosing between Pub/Sub and Streams

NeedUse
Fire-and-forget fan-out, lowest latencyPub/Sub
Guaranteed processing, replay, consumer groupsStreams
Both (notify now + process reliably)Publish to a stream, Pub/Sub-notify consumers

Released under the MIT License.