UNPKG

@firtoz/socka

Version:

Standard Schema–first WebSocket RPC for TypeScript — Bun, Hono, Node ws, Cloudflare Workers, Durable Objects

97 lines (75 loc) • 4.33 kB
# React + Cloudflare Durable Objects This page is a **minimal wiring guide** for the common stack: **one shared `defineSocka` contract**, a **Durable Object** extending **`SockaWebSocketDO`**, and a **React** client using **`useSockaSession`**. A full app with routing and persistence is **[chatroom-do](../../examples/chatroom-do)** in this repo. ## Install You need **`@firtoz/socka`**, **`@firtoz/socka/do`**, **`@firtoz/socka/react`**, and peer deps for Cloudflare and **`@firtoz/websocket-do`**—see **[Peers](./peers.md)** and **[Durable Objects](./durable-objects.md)** (Wrangler, `Env` types, **do not** hand-edit generated worker env `.d.ts` files). ## Shared contract (no client casts) Export **one** contract from a shared module and import the **same reference** in the worker and the browser. You should **not** need `as never`, `as unknown`, manual `send` types, or `InferSockaSend` in normal app code—if TypeScript widens or fails to infer, align **Zod (or other Standard Schema) output** with any hand-written types (see **[TypeScript and exact optional properties](./reference.md#typescript-and-exact-optional-properties)**) and re-export **types** from the schema with **`z.infer`** when possible. ```ts // shared/contract.ts import { defineSocka } from "@firtoz/socka/core"; import { z } from "zod"; export const roomContract = defineSocka({ calls: { list: { input: z.object({}).optional(), output: z.object({ items: z.array(z.string()) }), }, sendCursor: { input: z.object({ x: z.number(), y: z.number() }), // no `output` — fire-and-forget; see README “Call `output` shapes” }, }, pushes: { cursorBatch: z.object({ cursors: z.array(z.object({ x: z.number(), y: z.number() })), }), }, }); ``` **Output shapes** — For high-frequency messages (cursors, live drafts), **omit** `output`. Use **`output: z.void()`** when the client should **await** a server ack. See **[Client — Fire-and-forget](./client.md#fire-and-forget)** and **[Reference — Optional output](./reference.md#optional-output-fire-and-forget)**. ## Durable Object Subclass **`SockaWebSocketDO`**. Declare **`protected readonly contract`** and implement **`buildSockaSessionConfig`**. Full patterns: **[Durable Objects](./durable-objects.md)**, example **[`examples/chatroom-do/src/do.ts`](../../examples/chatroom-do/src/do.ts)**. ```ts // do.ts (sketch) import { SockaWebSocketDO, type SockaDoSessionConfigInput, } from "@firtoz/socka/do"; import { roomContract } from "./contract"; type SessionData = { userId: string }; export class RoomDo extends SockaWebSocketDO< typeof roomContract, SessionData, Env > { protected readonly contract = roomContract; protected buildSockaSessionConfig( ctx: Context<{ Bindings: Env }> | undefined, ): SockaDoSessionConfigInput<typeof roomContract, SessionData, Env> { return { handlers: { … }, handleClose: async () => {} }; } } ``` ## React client Use **`useSockaSession(contract, { url, pushHandlers? }, deps)`** with the **same** `roomContract` import. For **`pushHandlers`**, you can use **`satisfies Partial<InferSockaPushHandlers<typeof roomContract>>`**—see **[Pushes — Typing `pushHandlers`](./pushes.md#typing-pushhandlers)**. ```tsx // RoomClient.tsx (sketch; URL must exist before connecting — see Client “SSR and WebSocket URLs”) import { useSockaSession } from "@firtoz/socka/react"; import { roomContract } from "./contract"; function RoomClient({ url }: { url: string }) { const { ready, send } = useSockaSession( roomContract, { url, pushHandlers: { cursorBatch: (p) => { /* set state */ } } }, [url], ); // `await send.list({})` — request/response // `void send.sendCursor({ x, y })` — fire-and-forget; see observability in Client + Reference return null; } ``` Whiteboard-style contracts (ops + draft + cursors) are sketched in **[Collaborative realtime](./collaborative-realtime.md)**. ## See also - **[Client](./client.md)** — `useSockaSession`, **SSR and WebSocket URLs**, fire-and-forget **observability** - **[Reference](./reference.md)** — `InferSocka*` types, `reportError`, `SockaReportError` kinds - **[Durable Objects](./durable-objects.md)** — hibernation, `session.update()`, `createData` - **[chatroom-do](../../examples/chatroom-do)** — SQLite + Drizzle + UI