WebSocket · In depth

SocketRoute

Defines a WebSocket endpoint and owns its handlers, services, clients, and opt-in multiplayer policies. Routes can add bounded admission, transport limits, ordered work, heartbeat, rooms, resumable sessions, distribution, draining, metrics, and protocol negotiation without changing legacy routes.

Explain it like I’m five

The simple mental model

A SocketRoute is a room with its own door and rules. The URL chooses the room; message.type chooses which handler inside the room receives the message.

When it fits

When should I use it?

Create one whenever a WebSocket path represents a distinct protocol, trust boundary, workload, or group of multiplayer resources.

A practical API pattern

Follow the example

This pattern explains the named API. Application classes, credentials, and assets may need to be supplied; use a complete recipe for a runnable starting point.

Read this article as Markdown

JavaScript
import { SocketRoute } from 'redweb'
import { ChatHandler } from './handlers/ChatHandler.js'
import { ClockService } from './services/ClockService.js'

class ChatRoute extends SocketRoute {
  constructor() {
    super({
      path: '/chat',
      handlers: [ChatHandler],
      services: [ClockService],
      allowDuplicateConnections: true,
      websocketOptions: {
        maxPayload: 1024 * 1024,
        perMessageDeflate: false,
      },
    })
  }
}
  1. 1

    The /chat path selects this route during the WebSocket upgrade.

  2. 2

    Admission and capacity checks run before the connection becomes an active client.

  3. 3

    Messages are dispatched by type to BaseHandler instances while route services and registries share the same lifecycle.

Configuration

Choices you can make

  • path: WebSocket path (required)
  • handlers: array of handler classes (required)
  • services: array of SocketService subclasses (optional)
  • allowDuplicateConnections: allow multiple clients from the same IP
  • websocketOptions: options passed to ws WebSocketServer, such as maxPayload or perMessageDeflate; closeTimeout defaults to 5000 milliseconds (integer 1..2147483647) and bounds an already-started closing handshake, not healthy idle connections
  • admission: authenticate, validate origins, and optionally place a client before upgrade
  • maxPendingUpgrades: finite concurrent admission/negotiation work (default 64)
  • limits: connection, message-rate, pending-message, and outbound-buffer ceilings
  • orderedMessages: serialize each connection through a bounded queue
  • heartbeat: one route-level half-open connection monitor
  • rooms and sessions: bounded grouping and expiring application-issued ownership
  • distribution: optional bounded broker adapter; no broker is bundled or required
  • drainHandlers: track handler work and expose a cooperative shutdown signal
  • protocol: version negotiation, stable envelopes/error codes, and optional binary codecs
  • connections: optional connectedClients group for verified player facades and private page projection; enables rooms and handler draining by default
Surface area

Methods and members

constructor({ path, handlers, services, allowDuplicateConnections, websocketOptions })

Validates input, instantiates handlers/services, sets up a `ws` server for the path with any websocketOptions, and registers connection listeners.

addHandler(HandlerClass)

Adds another handler class unless a handler with the same name already exists.

handleConnection(socket, req)

Stores the client (deduping by IP unless allowed), decorates the socket with `sendJson`/`broadcast`, and wires close/error/message listeners.

handleMessage(socket, data)

Dispatches an already-parsed message object by type. Only literal false declines success; a deliberate rejection should send a correlated protocol error. Unexpected handler exceptions retain sanitized failure and connection-close behavior.

handleBinaryMessage(socket, buffer)

Handles binary frames separately from JSON text frames and delegates to a handler selected by acceptsBinary(socket, buffer).

handleClose(socket, ip)

Removes the client from the registry and triggers an optional `connectionCloseCallback`.

shutdown()

Marks the route draining, stops services, bounds handler/adapter cleanup, closes clients, and releases every route-owned resource.

beginDrain()

Flips readiness and rejects new upgrades before shutdown work begins.

isReady()

Reports whether the route is accepting upgrades and any required distribution adapter is healthy.

publish(type, payload)

Publishes through the optional bounded distribution adapter and resolves to a success boolean.

handleError(socket, error, ip)

Logs socket errors; override for custom reporting.