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 🙏

© 2025 – Pkg Stats / Ryan Hefner

payload-uwebsocket-plugin

v1.1.1

Published

A high-performance WebSocket plugin for Payload CMS using uWebSockets.js with Redis support for multi-instance deployments

Readme

uWebSocket Plugin for Payload CMS

A high-performance WebSocket plugin for Payload CMS using uWebSockets.js, providing real-time event broadcasting with Redis support for multi-instance deployments.

Features

  • High Performance - Built on uWebSockets.js, one of the fastest WebSocket implementations
  • JWT Authentication - Secure WebSocket connections using Payload's authentication
  • Redis Pub/Sub - Multi-instance support for horizontal scaling
  • Room-based Broadcasting - Subscribe to specific collections or custom rooms
  • Authorization Handlers - Per-collection authorization logic
  • Custom Message Handlers - Extend with your own WebSocket message types
  • Automatic Hooks - Integrates with Payload's afterChange and afterDelete hooks
  • TypeScript Support - Full type definitions included

Architecture

How It Works

┌─────────────────────────────────────────────────────────────────┐
│                         Client (Browser)                         │
│  WebSocket Connection: ws://localhost:3002?token=<JWT_TOKEN>    │
└────────────────────────────────┬────────────────────────────────┘
                                 │
                                 ▼
┌─────────────────────────────────────────────────────────────────┐
│                    uWebSocket Server (Port 3002)                 │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │  Authentication Layer (JWT Verification)                  │  │
│  └──────────────────────────────────────────────────────────┘  │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │  WebSocket Handlers                                       │  │
│  │  • upgrade - Authenticate connection                      │  │
│  │  • open - Register socket                                 │  │
│  │  • message - Handle client messages                       │  │
│  │  • close - Cleanup on disconnect                          │  │
│  └──────────────────────────────────────────────────────────┘  │
│  ┌──────────────────────────────────────────────────────────┐  │
│  │  Room Manager                                             │  │
│  │  • Collection Rooms: "collection:posts"                   │  │
│  │  • Custom Rooms: "room:custom-id"                         │  │
│  └──────────────────────────────────────────────────────────┘  │
└────────────────────────────────┬────────────────────────────────┘
                                 │
                                 ▼
┌─────────────────────────────────────────────────────────────────┐
│                      Redis Pub/Sub (Optional)                    │
│  Synchronizes events across multiple server instances           │
└────────────────────────────────┬────────────────────────────────┘
                                 │
                                 ▼
┌─────────────────────────────────────────────────────────────────┐
│                         Payload CMS Hooks                        │
│  • afterChange - Emit 'update' events                           │
│  • afterDelete - Emit 'delete' events                           │
└─────────────────────────────────────────────────────────────────┘

Installation

Install the plugin via npm:

npm install payload-uwebsocket-plugin

The plugin has the following dependencies:

  • uWebSockets.js - High-performance WebSocket server
  • ioredis - Redis client for multi-instance support
  • jsonwebtoken - JWT authentication

Peer dependency:

  • payload - ^2.0.0 || ^3.0.0 (tested with v2.30.1 and v3.64.0)

Configuration

1. Add Plugin to Payload Config

import { buildConfig } from "payload";
import { uWebSocketPlugin } from "payload-uwebsocket-plugin";

export default buildConfig({
  // ... other config
  plugins: [
    uWebSocketPlugin({
      enabled: true,
      includeCollections: ["posts", "users", "media"], // Collections to enable real-time events for
      redis: process.env.REDIS_URL
        ? {
            url: process.env.REDIS_URL,
          }
        : undefined,
      authorize: {
        // Optional: Add authorization handlers per collection
        posts: async (user, event) => {
          // Return true if user can receive updates about this document
          // Example: Only send updates for published posts or posts owned by user
          return (
            event.doc.status === "published" || event.doc.author === user.id
          );
        },
      },
    }),
  ],
});

2. Initialize WebSocket Server

In your server file (e.g., server.ts):

import express from "express";
import payload from "payload";
import { initUWebSocket } from "payload-uwebsocket-plugin";

const app = express();

// Initialize Payload
await payload.init({
  secret: process.env.PAYLOAD_SECRET!,
  express: app,
});

// Initialize uWebSocket server on a separate port
await initUWebSocket(3002);

// Start Express server
app.listen(3000, () => {
  console.log("Payload running on http://localhost:3000");
  console.log("WebSocket running on ws://localhost:3002");
});

3. Environment Variables

# Optional: Redis URL for multi-instance support
REDIS_URL=redis://localhost:6379

Client Usage

Connecting to WebSocket

// Get JWT token from Payload authentication
const token = localStorage.getItem("payload-token");

// Connect to WebSocket server
const ws = new WebSocket(`ws://localhost:3002?token=${token}`);

ws.onopen = () => {
  console.log("Connected to WebSocket");
};

ws.onmessage = (event) => {
  const message = JSON.parse(event.data);
  console.log("Received:", message);
};

Subscribing to Collection Updates

// Subscribe to posts collection updates
ws.send(
  JSON.stringify({
    type: "join-collection",
    collection: "posts",
  })
);

// Listen for updates
ws.onmessage = (event) => {
  const { type, collection, doc } = JSON.parse(event.data);

  if (type === "update" && collection === "posts") {
    console.log("Post updated:", doc);
    // Update your UI with the new data
  }

  if (type === "delete" && collection === "posts") {
    console.log("Post deleted:", doc.id);
    // Remove from your UI
  }
};

Unsubscribing from Collection

ws.send(
  JSON.stringify({
    type: "leave-collection",
    collection: "posts",
  })
);

How Events Flow

1. Document Update Flow

User updates document in Payload CMS
         ↓
Payload afterChange hook fires
         ↓
uWebSocketManager.emitEvent() called
         ↓
Authorization handler checks permissions (if configured)
         ↓
Event published to Redis (if configured)
         ↓
All connected clients in collection room receive update

2. Custom Room Flow

Client sends custom message (e.g., 'join-room')
         ↓
Custom message handler processes request
         ↓
User added to custom room
         ↓
Server can broadcast to all users in room

API Reference

Plugin Options

interface UWebSocketPluginOptions {
  enabled: boolean; // Enable/disable the plugin
  includeCollections?: string[]; // Collections to track
  redis?: {
    url: string; // Redis connection URL
  };
  authorize?: {
    [collection: string]: (user: any, event: any) => Promise<boolean>;
  };
}

Client Message Types

  • join-collection - Subscribe to collection updates
  • leave-collection - Unsubscribe from collection updates
  • Custom types can be added via message handlers

Server Event Types

  • update - Document was updated
  • delete - Document was deleted
  • Custom events can be emitted via the manager

Redis Multi-Instance Support

When Redis is configured, the plugin synchronizes events across multiple server instances:

Instance 1                    Redis                    Instance 2
    │                           │                           │
    │  Document updated         │                           │
    ├──────────────────────────►│                           │
    │  Publish to channel       │                           │
    │                           ├──────────────────────────►│
    │                           │  Subscribe receives event │
    │                           │                           │
    │                           │  Broadcast to clients ────┤

This ensures that clients connected to different server instances receive the same events.

Development vs Production

Development (Single Instance)

uWebSocketPlugin({
  enabled: true,
  includeCollections: ["posts", "users"],
  // No Redis needed for single instance
});

Production (Multi-Instance)

uWebSocketPlugin({
  enabled: true,
  includeCollections: ["posts", "users"],
  redis: {
    url: process.env.REDIS_URL, // Required for multi-instance
  },
});

Advanced Usage

Custom Message Handlers

You can add custom WebSocket message handlers:

import { getUWebSocketManager } from "payload-uwebsocket-plugin";

// After initializing the WebSocket server
const manager = getUWebSocketManager();

manager.registerCustomHandler("custom-action", async (ws, data) => {
  // Handle custom message type
  console.log("Custom action received:", data);

  // Send response back to client
  manager.sendToSocket(ws, {
    type: "custom-response",
    data: { success: true },
  });
});

Broadcasting to Custom Rooms

const manager = getUWebSocketManager();

// Broadcast to all clients in a custom room
manager.broadcastToRoom("room:custom-id", {
  type: "notification",
  message: "Hello everyone!",
});

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Changelog

See CHANGELOG.md for version history.

License

MIT © Bibek Thapa

Support

For any questions or issues, please open an issue.