chanx

Typed WebSockets that speak the same protocol in Python, TypeScript and Go.

Declare each message once, and chanx routes it to a typed handler, validates it, broadcasts it to groups and documents it as AsyncAPI, then generates typed clients from that document. Every implementation puts the same frames on the wire, so a Django server, a Go service and a React app can all talk to each other.

  1. chanx, Python
    class ChatMessage(BaseMessage):
        action: Literal["chat"] = "chat"
        payload: ChatPayload
  2. chanx-js, TypeScript
    const Chat = defineMessage('chat',
      z.object({ text: z.string() }),
    );
  3. chanx-go, Go
    type ChatMessage struct {
    	chanx.BaseMessage `action:"chat"`
    	Payload ChatPayload `json:"payload"`
    }
  4. On the wire, from any of them
    {"action": "chat", "payload": {"text": "hi"}}

Four projects, one protocol

Use one, or mix them across your stack. Each has its own docs.

The server and the client don't have to share a language

Every chanx server serves an AsyncAPI document built from its handlers. Point a generator at it and you get a typed client, whatever the server is written in.

  • Python serverDjango, FastAPI
  • TypeScript serverNode, Bun, Deno
  • Go servernet/http, gin, echo, Fiber
/asyncapi.json AsyncAPI 3.0, generated from your handlers
  • TypeScript clientFor your frontend: React, Vue, Svelte, Solid, the plain browser, or Node npx @chanx-js/codegen
  • Python clientscripts, services, testschanx generate-client
  • Go clientCLIs, bots, load testsgo run go.chanx.dev/cmd/chanx-codegen@latest

Servers can mix too: Node and Python servers can share one Redis channel layer, so a broadcast from either reaches the clients of both.

From handler to typed client

Write a handler, open the AsyncAPI docs generated from it, generate a client, and watch the compiler catch a typo.

Writing another implementation?

The protocol is small: JSON text frames with an action and a payload, plus request ids, topics and a single error shape. The spec lists every frame, so a new implementation can be checked against the three above.

Read the protocol spec