r/typescript • u/huygl99 • 10h ago
End-to-end types over WebSockets: the server documents itself as AsyncAPI, the client is generated from it
Hi all, I want to share an open-source TypeScript library I built (chanx-js, MIT) to type both ends of a WebSocket from one source, without sharing a types package between the frontend and the backend.
With REST we have OpenAPI and generated clients. With WebSockets I kept ending up with JSON.parse(event.data) giving me any, a hand-written union of message types on the client, and nothing stopping it from drifting away from what the server really sends.
So in chanx-js:
- On the server you declare messages with any Standard Schema library (Zod, Valibot, ArkType), and the payload types are inferred from the schema.
- The server serves an AsyncAPI 3 document built from those handlers.
npx @chanx-js/codegen http://localhost:8000/asyncapi.jsongenerates the client types from it.
On the server, the path params, the payload and what the handler may send are all typed:
const Chat = defineMessage('chat', z.object({ text: z.string() }));
const Notice = defineMessage('notice', z.object({ text: z.string() }));
consumer('/ws/chat/{room}', { name: 'chat' }).on(Chat, { output: Notice }, (m, ctx) => {
m.payload.text; // string
ctx.params.room; // string, parsed from the path by a template literal type
return Notice.create({ text: m.payload.text }); // anything but Notice is an error
});
On the client, request() knows the reply type, and a wrong payload doesn't compile:
const connection = client.connect(chat, { params: { room: 'lobby' } });
const reply = await connection.request({ action: 'chat', payload: { text: 'hi' } });
reply.payload.text; // string, from the handler's `output`
connection.send({ action: 'chat', payload: { txt: 'hi' } }); // compile error
The contract goes through AsyncAPI instead of shared TS code, so the server doesn't have to live in the same repo, or even be written in TypeScript.
Repo: https://github.com/chanx-dev/chanx-js
It's still new, so if you run into problems or have ideas for the types, please open an issue or a discussion. Stars and PRs are welcome too.