Skip to content

Overview

Register WebSocket endpoints on host.ws with inline handlers or controller classes.

AI generated, pending review Updated 10 days ago · Flare 0.3

The WebSocket arc exposes two registration surfaces on host.ws, mirroring HTTP:

StyleAPIDocs
Inline handlershost.ws.route(path, opts?)Inline handlers
Controller classeshost.ws.controller(path, Cls) or host.ws.controller(path, opts, Cls)Controller classes

Both forms share route options: contract or loose descriptor fields (incoming, outgoing, params, query, subprotocols), state tokens, optional channel selector, and hibernate on Durable Object routes. The function form additionally accepts a named inject map; controllers use static deps instead.

host.build() compiles registrations into a router. When a client requests a WebSocket upgrade, the runtime calls the arc’s upgrade entry, matches the pathname, parses declared params and query, and returns a live connection (or rejects the handshake when nothing matches).

On Node, unmatched paths get a 404 before the handshake completes. Invalid param or query values reject the upgrade at match time.

Inline routes attach behaviors to the handle route() returns:

  • open runs once when the connection reaches OPEN (inbound messages wait if it is async)
  • message runs per inbound frame (validated when incoming is declared)
  • close runs at terminal close
  • error runs on transport errors (a close still follows)

Controller classes override the same four methods on WebSocketControllerBase.

Both forms can attach one pre-handshake hook with .upgrade() on the handle that route() or controller() returns. It is the only WebSocket moment with request context: the handler receives a WebSocketUpgrade view of the HTTP request (url plus case-insensitive header()) and a scope carrying typed input, injected services, and a state writer. It may be async, and it decides the upgrade by what it returns:

  • Nothing: proceed. Values written to scope.state seed the connection’s ws.state, so an identity verified at upgrade reaches every handler without being re-derived in open.
  • A FlareResponse: deny before the handshake. HTTP-speaking clients (curl, server-side clients) read the real status and body; on Node it is written on the raw socket in place of the 101.
  • A WebSocketRefusal(code, reason): accept, then immediately close. A denied handshake exposes nothing to browser JavaScript, but a close frame’s code and reason arrive in the browser’s close event, so this is the refusal a browser can read. The code must be 1000 or an application code in 3000-4999, and the reason at most 123 bytes of UTF-8.

The hook resolves dependencies from the same per-connection container the handlers use, so a service it touches is an ordinary connection-scoped service. On a denial or refusal the container is disposed and the route’s channels and controller are never touched.

Upgrade hooks are front-door only. A hooked route on a Durable Object’s .ws arc fails host.build() with WS_UPGRADE_IN_DURABLE_OBJECT; gate a Durable Object’s WebSocket routes with the mount’s resolve handler instead, which already runs in the Worker.

Registration and examples: Inline handlers and Controller classes.

On Cloudflare, a registered Durable Object carries its own .ws surface with these same route forms. Durable Objects covers registration, mounting, and upgrades.