WebSocket
Okapi provides WebSocket support through the separate okapi-ws package, enabling real-time, bidirectional communication between server and clients. This lightweight, framework-agnostic package offers a clean API and seamless integration with both Okapi and standard Go HTTP servers.
It is not part of the okapi module, so add it to your own go.mod.
Installation
go get github.com/jkaninda/okapi-ws
Quick Start
Basic Usage with Okapi
The upgrade needs the raw http.ResponseWriter and *http.Request, which
c.Response() and c.Request() provide:
package main
import (
"log"
"net/http"
"github.com/jkaninda/okapi"
okapiws "github.com/jkaninda/okapi-ws"
)
// WebSocket upgrades the HTTP connection to WebSocket.
// Config is optional; pass nil to use default settings.
func WebSocket(config *okapiws.WSConfig, c *okapi.Context) (*okapiws.WSConnection, error) {
upgrader := okapiws.NewWSUpgrader(config)
return upgrader.Upgrade(c.Response(), c.Request(), nil)
}
// WebSocketWithHeaders upgrades with additional response headers.
func WebSocketWithHeaders(config *okapiws.WSConfig, headers http.Header, c *okapi.Context) (*okapiws.WSConnection, error) {
upgrader := okapiws.NewWSUpgrader(config)
return upgrader.Upgrade(c.Response(), c.Request(), headers)
}
func main() {
app := okapi.Default()
app.Get("/", func(c *okapi.Context) error {
return c.OK(okapi.M{"message": "Hello from Okapi Web Framework!"})
})
app.Get("/ws", handleWebSocket)
if err := app.Start(); err != nil {
panic(err)
}
}
func handleWebSocket(c *okapi.Context) error {
ws, err := WebSocket(nil, c)
if err != nil {
return err
}
defer func() {
if err := ws.Close(); err != nil {
log.Printf("error closing WebSocket: %v", err)
}
}()
ws.OnMessage(func(msg *okapiws.WSMessage) {
log.Printf("[%d] %s", msg.Type, msg.Data)
// Echo the message back
_ = ws.Send(msg.Data)
})
ws.OnError(func(err error) {
log.Printf("WebSocket error: %v", err)
})
ws.Start()
// Block until the connection is closed
<-ws.Context().Done()
return nil
}
okapi.ResponseWriter implements Hijack(), which is what the upgrade needs, so
passing c.Response() straight to the upgrader works.
Using With Go net/http
This example shows integration using Go's standard library:
package main
import (
"log"
"net/http"
okapiws "github.com/jkaninda/okapi-ws"
)
func main() {
http.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
upgrader := okapiws.NewWSUpgrader(nil) // nil = use default config
ws, err := upgrader.Upgrade(w, r, nil)
if err != nil {
http.Error(w, "WebSocket upgrade failed", http.StatusBadRequest)
return
}
defer ws.Close()
ws.OnMessage(func(msg *okapiws.WSMessage) {
log.Printf("[%d] %s", msg.Type, msg.Data)
_ = ws.Send(msg.Data) // Echo
})
ws.OnError(func(err error) {
log.Printf("WebSocket error: %v", err)
})
ws.Start()
<-ws.Context().Done() // Block until closed
})
log.Println("Listening on :8080...")
log.Fatal(http.ListenAndServe(":8080", nil))
}
WebSocket routes are not logged by Okapi's access-log middleware: it skips connection upgrades, as it does SSE responses.