Source: https://www.hype-stack.dev/docs/backend/websockets

# How to Add WebSockets

WebSocket events are defined in `src/sockets/<domain>/index.ts` using `SocketRouter`. They're separate from HTTP routes
but follow the same domain-based organization.

## Defining events

Use `createSocketRouter()` to declare emitters (server pushes to client) and listeners (client sends to server):

```ts
import { createSocketRouter } from "@backend/libs/websocket/socket-router";
import { wsValidate } from "@backend/libs/websocket/ws-validate";
import { z } from "zod";

const markReadSchema = z.object({ notificationId: z.string() });

export const notificationSockets = createSocketRouter()
  .emitter("new")<WsNotificationPayload>()
  .emitter("count")<WsUnreadCountPayload>()
  .listener("mark-read", wsValidate(markReadSchema), async (c) => {
    await markReadMutation({
      notificationId: c.data.notificationId,
      userId: c.userId,
    });
  });
```

- **Emitters** are type-only declarations. The actual push happens via `wsEmit` in your feature code.
- **Listeners** have an inline handler that processes incoming client messages. Use `wsValidate(schema)` for Zod
  validation.

## Registering socket routes

Mount socket routers in `src/sockets/index.ts`:

```ts
const sockets = new SocketRouter({ authenticate: wsAuthenticate })
  .socket("/notification", notificationSockets)
  .socket("/invitation", invitationSockets);

export const registerSockets = (app: Hono) => {
  app.route("/ws", sockets.routes());
};
```

## Emitting from features

Use `wsEmit` from `@backend/sockets/emit` for type-safe server-side pushes:

```ts
import { wsEmit } from "@backend/sockets/emit";

wsEmit.toUser({ userId, topic: "notification/new", data: notification });
wsEmit.toUsers({ userIds, topic: "notification/new", data: notification });
wsEmit.toOrganization({ organizationId, topic: "invitation/new", data: invitation });
wsEmit.broadcast({ topic: "notification/count", data: { unreadCount: 0 } });
```

Topics and payloads are type-checked at compile time. A typo in the topic or wrong payload shape is a type error.

## Frontend integration

Socket event types are exported from the backend and picked up by the frontend's `socketSdk` automatically. No manual
type wiring needed. See [Frontend WebSockets](/docs/frontend/websockets).

## Adding a new socket event

1. Create `src/sockets/<domain>/index.ts` with `createSocketRouter()`
2. Declare emitters and listeners
3. Register in `src/sockets/index.ts` with `.socket("/prefix", router)`
4. Export payload types for the frontend
5. Use `wsEmit` in your feature code to push events
