Why chanx-go?
Go has excellent WebSocket libraries, gorilla/websocket and coder/websocket, but they stop at the socket. Everything after the upgrade is left to you, and every team writes the same loop:
go
for {
var msg struct {
Action string `json:"action"`
Payload json.RawMessage `json:"payload"`
}
if err := conn.ReadJSON(&msg); err != nil {
return
}
switch msg.Action {
case "chat":
var p ChatPayload
if json.Unmarshal(msg.Payload, &p) != nil || p.Text == "" {
conn.WriteJSON(map[string]string{"error": "bad payload"})
continue
}
hub.Broadcast(room, p) // your own hub, and one process only
case "ping":
// ...
}
}Then it grows: a hub for rooms, Redis pub/sub once there is more than one process, a validation helper per message, and API docs written by hand that fall behind the code.
With chanx-go, the same chat is a struct and a method:
go
type ChatMessage struct {
chanx.BaseMessage `action:"chat"`
Payload ChatPayload `json:"payload"`
}
func (ChatConsumer) HandleChat(ctx *chanx.Context, m ChatMessage) error {
return ctx.Broadcast(ChatNotification{Payload: m.Payload})
}The loop, the validation, the hub and the docs are chanx-go's job, written once and tested.
What you get
| You write | chanx-go does |
|---|---|
| A struct per message | infers its JSON Schema, validates every frame, answers bad ones with an error frame |
| A method per handler | routes each frame by action, echoes the ref on replies, recovers panics |
Groups and ctx.Broadcast | joins and leaves groups, fans out across processes over the layer |
| A topic struct | subscribe, unsubscribe, authorization, per-topic handlers, over one socket |
Authenticate | runs it once, reports the result, closes with 4401 or 4403 on failure |
| Nothing more | an AsyncAPI 3 document, a docs page, and typed clients generated from it |
Compared with what Go has
| Project | What it is | How it differs |
|---|---|---|
| gorilla/websocket, coder/websocket | WebSocket connections | The layer below. chanx-go runs on either |
| melody | sessions and broadcast | No typed messages, no cross-process groups, no documentation |
| Centrifuge | a mature real-time pub/sub server | Its own protocol and SDKs; a broker, not typed request and reply handlers. The right pick for a pure pub/sub server |
| Socket.IO ports | events and rooms | Untyped payloads, no schema, the Socket.IO protocol |
| asyncapi3, swaggest/go-asyncapi | AsyncAPI documents from Go types | Documents only, declared beside the server. chanx-go's comes from the handlers that run |
One protocol, three languages
chanx-go is a third implementation of the chanx protocol, after Python and TypeScript. That brings more than familiarity:
- Clients work unchanged.
@chanx-js/clientand the clients generated by@chanx-js/codegenor Python'schanx generate-clienttalk to a Go server as they do to a Python one. - One Redis, every language. The Redis layer is wire-compatible with channels_redis, fast-channels and
@chanx-js/layers. A Go worker can push to sockets held by a Django server, and the other way round. - Parity is tested, not claimed. Validation errors, group names, key casing, the Redis encodings and the whole AsyncAPI document are checked against values recorded from Python chanx. The few deliberate differences are listed in Differences from Python.
Who it is for
- Go teams building chat, notifications, live dashboards, collaboration or AI streaming, who want structure rather than a raw socket.
- Teams already on chanx in Python who move a service to Go, or run both side by side.
- Frontend teams who want a typed client generated from the server, not written by hand.
Next: the quick start.