typed actors
for realtime apps
Define stateful actors with typed methods, events, and state — then call them from any client with full end-to-end type safety over WebSockets.
end-to-end typesafety
With Zocket, your actor state, methods, and events are fully typed from server to client using Zod schemas. Never worry about sending the wrong data again.
server.ts
import { z } from "zod";import { actor, createApp } from "@zocket/core";const ChatRoom = actor({state: z.object({messages: z.array(z.object({from: z.string(),text: z.string(),})).default([]),}),methods: {sendMessage: {input: z.object({ from: z.string(), text: z.string() }),handler: ({ state, input, emit }) => {state.messages.push(input);emit("newMessage", input);},},},events: {newMessage: z.object({ from: z.string(), textt: z.string() }), // whoops, typo},});export const app = createApp({ actors: { chat: ChatRoom } });
messages: Message[]
online: number
client.ts
import { createClient } from "@zocket/client";import type { app } from "./server";const client = createClient<typeof app>({url: "ws://localhost:3000",});const room = client.chat("general");await room.sendMessage({ from: "Alice", text: "Hi!" });room.state.subscribe((s) => {console.log(s.|) // autocomplete works like a charm});
realtime on steroids
Batteries included. Type-safe by default.
Middleware
Add authentication, logging, and rate limiting with fully type-safe middleware. Context flows through to your method handlers with complete type inference.
Implementing authentication for realtime apps has never been easier.
middleware.ts
import { z } from "zod";import { actor, createApp } from "@zocket/core";import { validateToken } from "./auth";const PrivateRoom = actor({state: z.object({messages: z.array(z.object({user: z.string(),text: z.string(),})).default([]),}),// Middleware runs before every method callmiddleware: async ({ headers, next }) => {const token = headers.get("authorization");const user = await validateToken(token);if (!user) throw new Error("Unauthorized");return next({ user });},methods: {send: {input: z.object({ text: z.string() }),handler: ({ state, input, ctx }) => {// ctx.user is fully typed from middlewarestate.messages.push({user: ctx.user.name,text: input.text,});},},},});
Error Handling
Throw typed errors from method handlers and catch them on the client with full type information. Errors include codes and messages for clean, predictable error handling.
Input Validation
Every method input is validated at runtime using Zod schemas. Invalid payloads are rejected before reaching your handler, keeping your actor state consistent and your application safe.
client.ts
const room = client.game("room-1");try {await room.join({ name: "Alice" });} catch (err) {if (err.code === "FORBIDDEN") {console.log(err.message); // "Room is full!"}}
server.ts
const GameRoom = actor({state: z.object({players: z.array(z.string()).default([]),}),methods: {join: {input: z.object({ name: z.string() }),handler: ({ state, input }) => {if (state.players.length >= 4) {throw new ZocketError({code: "FORBIDDEN",message: "Room is full!",});}state.players.push(input.name);},},},});
State Subscriptions
Subscribe to actor state with fine-grained selectors. Components only re-render when the slice they care about changes. State is synchronized via efficient JSON patches over the wire.
Multiple clients can subscribe to the same actor instance, each receiving real-time updates as state changes.
state.ts
// Subscribe to full actor stateconst room = client.chat("general");room.state.subscribe((state) => {console.log(state.messages);});// React: selector-based subscriptionsconst messages = useActorState(room, (s) => s.messages);const online = useActorState(room, (s) => s.online);// Multiple actor instances — each with independent stateconst lobby = client.game("lobby");const room1 = client.game("room-1");const room2 = client.game("room-2");// Clean up when doneroom.$dispose();
ready to build?
Get started with Zocket in minutes. Define your first actor, connect a client, and ship realtime features with full type safety.
1. Install
bun add @zocket/core @zocket/server @zocket/client zod
2. Define Actors
Create typed actors with state schemas, methods, events, and lifecycle hooks — all validated by Zod.
3. Ship It
Connect clients with full type inference. Use React hooks for selector-based state subscriptions.