Define message contracts with Zod or Valibot and get end-to-end TypeScript safety for WebSocket RPC and pub/sub across Bun, Cloudflare, Node.js, and browsers.
Docs → https://kriasoft.com/ws-kit/
- Type inference from schema to handler, errors, and client calls
- RPC + pub/sub + middleware + lifecycle hooks in one router
- Pluggable validators/adapters (Bun, Cloudflare, Redis, in-memory)
- Test harness with fake connections, clock, and event capture
- Universal client with auto-reconnect, retries, and offline queueing
# With Zod on Bun (recommended) bun add @ws-kit/zod @ws-kit/bun bun add zod bun @types/bun -D # Valibot (smaller bundles) bun add @ws-kit/valibot @ws-kit/bun bun add valibot bun @types/bun -Dimport { z, message, createRouter, withZod } from "@ws-kit/zod"; import { serve } from "@ws-kit/bun"; const Ping = message("PING", { text: z.string() }); const Pong = message("PONG", { reply: z.string() }); const router = createRouter().plugin(withZod()); router.on(Ping, (ctx) => { ctx.send(Pong, { reply: `Got: ${ctx.payload.text}` }); }); serve(router, { port: 3000, authenticate(req) { const token = req.headers.get("authorization"); return token ? { userId: "u_123" } : undefined; }, });import { rpc, message, wsClient } from "@ws-kit/client/zod"; import { z } from "@ws-kit/zod"; const Hello = rpc("HELLO", { name: z.string() }, "HELLO_OK", { text: z.string(), }); const Broadcast = message("BROADCAST", { data: z.string() }); const client = wsClient({ url: "ws://localhost:3000" }); await client.connect(); const reply = await client.request(Hello, { name: "Ada" }); console.log(reply.payload.text); // typed as string client.on(Broadcast, (msg) => { console.log(msg.payload.data); });- Docs: https://kriasoft.com/ws-kit/
- Examples:
examples/ - Packages:
packages/ - Support: Discord
MIT







