021 Project 21: WebSocket Chat
021 Build a WebSocket Chat Server
A minimal real-time chat backend with broadcast hub.
Setup
go mod init example.com/gochat
go get github.com/gorilla/websocket@latestclients <-> ws hub <-> broadcast channel
Full main.go
package main
import (
"log"
"net/http"
"sync"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}
type hub struct {
mu sync.Mutex
clients map[*websocket.Conn]struct{}
}
func (h *hub) add(c *websocket.Conn) {
h.mu.Lock(); defer h.mu.Unlock()
h.clients[c] = struct{}{}
}
func (h *hub) del(c *websocket.Conn) {
h.mu.Lock(); defer h.mu.Unlock()
delete(h.clients, c)
}
func (h *hub) broadcast(msg []byte) {
h.mu.Lock(); defer h.mu.Unlock()
for c := range h.clients {
_ = c.WriteMessage(websocket.TextMessage, msg)
}
}
func main() {
h := &hub{clients: map[*websocket.Conn]struct{}{}}
http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
c, err := upgrader.Upgrade(w, r, nil)
if err != nil {
return
}
h.add(c)
defer func() { h.del(c); _ = c.Close() }()
for {
_, msg, err := c.ReadMessage()
if err != nil {
return
}
h.broadcast(msg)
}
})
log.Println("ws chat on :8090 /ws")
log.Fatal(http.ListenAndServe(":8090", nil))
}Run
go run .
# Connect from browser/websocket client to ws://localhost:8090/wsStep-by-Step Explanation
- Define request and response contracts first.
- Validate inbound input before doing any state changes.
- Keep handler logic short and move reusable logic into helper functions.
- Add timeouts and clear error paths.
- Return consistent responses and status codes.
Code Anatomy
- Handlers parse input, call domain logic, write response.
- Shared state uses synchronization where needed.
- Transport concerns stay separate from business rules.
Learning Goals
- Build reliable service endpoints in Go.
- Understand API ergonomics and operational safety.
- Prepare code structure for tests and persistence later.