Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/cloudflare/partykit/llms.txt

Use this file to discover all available pages before exploring further.

partysub adds scalable publish/subscribe messaging to Cloudflare Workers by using Durable Objects as coordinating nodes. Clients connect via PartySocket and subscribe to one or more named topics; any connected client (or an HTTP POST) can publish a message that fans out to all matching subscribers in real time.
partysub is experimental and is not yet recommended for production use. The API and design are subject to change.

Installation

npm install partyserver partysub partysocket

Function: createPubSubServer

Import from partysub/server. Call this once at the top level of your Worker to generate the Durable Object class and the request router.
import { createPubSubServer } from "partysub/server";

const { PubSubServer, routePubSubRequest } = createPubSubServer({
  binding: "PubSub",
  nodes: 100,
  locations: {
    eu: 1,
    wnam: 3
  }
});

Options

binding
string
required
The exact name of the Durable Object binding declared in wrangler.jsonc. The client uses the lowercase form of this value as the party name.
nodes
number
default:"1"
Number of Durable Object nodes to spin up per room id. More nodes allow higher message throughput. Each node handles a subset of connections.
locations
Record<string, number>
Optional map of Cloudflare location hints to node weights. The weight controls how many nodes are allocated to that region. Clients connecting from a listed region are routed to a nearby node; others are routed randomly.
locations: {
  eu: 1,    // 1 node in Europe
  wnam: 3   // 3 nodes in Western North America
}
jurisdiction
string
Optional data jurisdiction (e.g. "eu"). Cannot be combined with locations. Not yet fully implemented.

Return value

createPubSubServer returns an object with two exports:
PubSubServer
DurableObject class
The Durable Object class to export from your Worker and reference in wrangler.jsonc.
routePubSubRequest
(request: Request, env: Env) => Promise<Response | null>
Routes incoming WebSocket upgrade requests and HTTP POST publish requests. Returns null when the request path does not match a pub/sub route, letting you fall through to your own handlers.

PubSubServer

Export PubSubServer from your Worker entrypoint so the Cloudflare runtime can instantiate it as a Durable Object:
export { PubSubServer };

routePubSubRequest

routePubSubRequest(request: Request, env: Env): Promise<Response | null>
Place this inside your Worker’s fetch handler. It handles:
  • WebSocket upgrades — clients connecting via PartySocket
  • HTTP POST requests — server-side message publishing
export default {
  async fetch(request, env) {
    const pubSubResponse = await routePubSubRequest(request, env);
    return pubSubResponse || new Response("Not found", { status: 404 });
  }
};

wrangler.jsonc Configuration

Declare PubSubServer as a Durable Object binding with SQLite storage and add a migration entry:
{
  "durable_objects": {
    "bindings": [
      {
        "name": "PubSub",
        "class_name": "PubSubServer"
      }
    ]
  },
  "migrations": [
    {
      "tag": "v1",
      "new_sqlite_classes": ["PubSubServer"]
    }
  ]
}

Client Connection with PartySocket

Use PartySocket to connect from the browser or Node.js. The party value must be the lowercase form of the binding name.
import { PartySocket } from "partysocket";

const ws = new PartySocket({
  host: "...",       // your Worker host, defaults to window.location.host
  party: "pubsub",   // lowercase form of the binding name ("PubSub" → "pubsub")
  room: "default",   // the channel / room name
  query: {
    // subscribe to specific topics (omit to receive all)
    topics: [
      "topic-abc",   // exact topic name
      "prefix:*"     // wildcard: matches any topic starting with "prefix:"
    ]
  }
});
party
string
required
Lowercase form of the binding name passed to createPubSubServer.
room
string
required
The channel name. All clients in the same room share the same node pool.
query.topics
string[]
Array of topic strings to subscribe to. Each entry is either an exact topic name (e.g. "topic-abc") or a prefix wildcard (e.g. "prefix:*"). Omit to subscribe to all topics.

Listening for messages

ws.addEventListener("message", (event) => {
  console.log(event.topic, event.data);
});

Publishing Messages

Over WebSocket

Send a JSON object with topic and data fields:
ws.send(JSON.stringify({ topic: "topic-abc", data: "hello world" }));

Over HTTP (server-side publish)

Use PartySocket.fetch for server-to-client publishing without a persistent WebSocket connection:
PartySocket.fetch(
  {
    host: window.location.host,
    party: "pubsub",
    room: "default"
  },
  {
    method: "POST",
    body: JSON.stringify({ topic: "topic-abc", data: "hello world" })
  }
);
Both methods accept the same JSON body shape:
topic
string
required
The topic to publish on. Subscribers with matching exact or wildcard subscriptions will receive the message.
data
any
required
The message payload. Can be any JSON-serializable value.

React: usePartySocket

Use the usePartySocket hook from partysocket/react for a React-friendly subscription:
import { usePartySocket } from "partysocket/react";

function App() {
  usePartySocket({
    party: "pubsub",
    room: "default",
    query: {
      topics: [
        "topic-abc",
        "prefix:*"
      ]
    },
    onMessage: (event) => {
      console.log(event.topic, event.data);
    }
  });

  return <div>...</div>;
}
The party value must always be the lowercase version of the Durable Object binding name set in createPubSubServer. For a binding named "PubSub", use party: "pubsub".

Build docs developers (and LLMs) love