Contracts
Type WebSocket messages and upgrade inputs with socketContract and scope.input.
WebSocket contracts describe what a route accepts and emits. socketContract is the "ws" kind of the shared contract core, the sibling of httpContract for HTTP routes.
Attach a branded entry with contract: MySocket.chat in route options, or spell the same fields loose in the options object. Do not pass both.
Descriptor fields
Section titled “Descriptor fields”| Field | Purpose | Typed into |
|---|---|---|
incoming | Schema for each inbound message | scope.input.message on message handlers |
outgoing | Schema for ws.send arguments | Outbound serialization on the connection |
params | Upgrade path params (/chat/:room) | scope.input.params at connect time |
query | Upgrade URL query primitives | scope.input.query at connect time |
subprotocols | Accepted subprotocol tokens | Handshake picks the first match |
When incoming is omitted, scope.input.message is a FlareWebSocketMessage wrapper (use .text(), .json(), .raw, .isBinary). When outgoing is omitted, send accepts raw string or Uint8Array.
Define a socketContract
Section titled “Define a socketContract”import { socketContract } from "@flare-ts/core";import { int, schema, str } from "@flare-ts/lib/schema";
const MsgIn = schema({ text: str });const MsgOut = schema({ text: str });
export const Chat = socketContract({ chat: { incoming: MsgIn, outgoing: MsgOut, params: { room: str }, query: { page: int }, subprotocols: ["chat.v1"], },});Register the entry on a route:
host.ws.route("/chat/:room", { contract: Chat.chat }) .open((_ws, scope) => { const room = scope.input.params.room; const page = scope.input.query.page; }) .message((ws, scope) => { ws.send({ text: scope.input.message.text }); });scope.input shape
Section titled “scope.input shape”Connect-time input is stable for the connection’s life:
scope.input.params // typed path paramsscope.input.query // typed query map (or URLSearchParams when undeclared)On message, scope.input adds the validated payload:
scope.input.message // validated inbound value, or FlareWebSocketMessageThis mirrors HTTP: per-invocation payload lives on scope.input, not as a separate handler argument shape from HTTP.
Validation timing
Section titled “Validation timing”- Params and query parse at upgrade match time. Bad values reject the handshake.
- Inbound messages validate on each
messageevent before your handler runs. On a typed route, a message that is not valid JSON or fails theincomingschema is logged and the connection closes with code 1008; the handler does not run. Untyped routes wrap the raw message and never reject at decode. - Outbound values serialize through
outgoingwhen declared.
Build-time validation catches contract kind mismatches (for example passing an httpContract entry to a WebSocket route).
Related
Section titled “Related”- HTTP contracts:
httpContractand request descriptors - Inline handlers: attach contracts to routes
- Primitives and builders: schema tokens used in descriptors