Handlers, not if/else
Each action gets a typed handler. Payloads are validated by Zod, Valibot, ArkType or any Standard Schema before your code sees them.
Declare your messages once. chanx-js routes, validates, broadcasts and documents them on the server, and generates a typed client for the browser, your framework or another Node service.
Install the server, with ws for Node's HTTP server and Zod for payloads:
npm install @chanx-js/server ws zodThen the client, and generate its types from the running server:
npm install @chanx-js/client
# writes src/generated
npx @chanx-js/codegen http://localhost:8000/asyncapi.jsonimport { createServer } from 'node:http';
import { z } from 'zod';
import {
PingMessage,
PongMessage,
consumer,
createChanx,
defineMessage,
} from '@chanx-js/server';
import {
asyncapiMiddleware,
attachChanx,
} from '@chanx-js/server/node';
const Chat = defineMessage('chat', z.object({ text: z.string() }));
const Notice = defineMessage(
'notice',
z.object({ text: z.string() }),
);
const chat = consumer('/ws/chat/{room}', {
name: 'chat',
emits: [Notice],
groups: (conn) => [`room.${conn.params.room}`],
})
.on(PingMessage, { output: PongMessage }, () =>
PongMessage.create(),
)
.on(Chat, (m, ctx) =>
ctx.broadcast(Notice.create({ text: m.payload.text })),
);
const chanx = createChanx({ consumers: [chat] });
// docs at /asyncapi
const server = createServer(asyncapiMiddleware(chanx));
attachChanx(server, chanx);
server.listen(8000);import { createClient } from '@chanx-js/client';
import { chat } from './src/generated/index.js';
const client = createClient({ baseUrl: 'ws://localhost:8000' });
const connection = client.connect(chat, {
params: { room: 'lobby' },
});
connection.on('notice', (message) =>
console.log(message.payload.text),
);
// typed: PongMessage
const pong = await connection.request({ action: 'ping' });
connection.send({ action: 'chat', payload: { text: 'hello' } });Run the server, then the client: it prints hello, broadcast back to everyone in lobby. From here, build out the server or use the client in your framework.